Theme Toggle

@sushindustries/ui

A segmented control that reports which option was pressed and knows nothing about themes.

4 min read
View as Markdown
Viewport width
Desktop100%whatever the page has
tanstacktanstack add https://adamjurek.com/r/tanstack/theme-toggle.jsonshadcnpnpm dlx shadcn@latest add https://adamjurek.com/r/shadcn/theme-toggle.json
<ThemeToggle
	options={[
		{ id: "light", label: "Light", icon: "sun" },
		{ id: "dark", label: "Dark", icon: "moon" },
		{ id: "system", label: "System", icon: "contrast" },
	]}
	value={theme}
	onChange={setTheme}
/>
tsx

It knows nothing about themes

It renders choices and reports which was pressed. It does not touch the document, does not store anything, and has never heard of light or dark - which is what lets the same control switch a density, a language or a layout.

Persisting is the host's problem on purpose. A cookie, a server function, an account row and a localStorage key are four answers with four different trade-offs, and a component that picked one would be wrong in three codebases out of four.

The attribute must already be on `<html>` before this mounts

A toggle that applies the theme in an effect guarantees a flash: the server paints one theme, the effect corrects it, and everyone sees both. This only changes an attribute that was already correct in the first byte.

Where this is used

WhereWhat
The nav, right-hand endswitching this site's theme
theme.schemas.tsthe cookie name, the values, the max-age
theme.functions.tsreads the cookie on the server, so the first byte is right
packages/atoms/theming.mdwhy a cookie and not a media query

Install

tanstack add https://adamjurek.com/r/tanstack/theme-toggle.json
shell
pnpm dlx shadcn@latest add https://adamjurek.com/r/shadcn/theme-toggle.json
shell
pnpm add @sushindustries/ui @sushindustries/atoms
shell

What you get

Version0.1.0
Categorylayout · Page structure
Filestheme-toggle.tsx, icon.tsx
DependenciesNone
Tagsa11y, keyboard, ssr, no-deps

No runtime dependencies

It brings nothing with it beyond the stylesheet.