Skip to content
Template Empire
Browse components

Theme Palette Dock

A free React theme palette switcher for Next.js and Tailwind: pick a named colour theme from an accessible swatch dock.

Appearance

Midnight

Ink-black surfaces with one cool blue accent, tuned for late sessions and dim rooms.

Interface theme

Syncs across your devices

Customise

36px
380
18px

Code

<ThemePaletteDock themes={themes} value={theme} onValueChange={setTheme} />

Theme Palette Dock is a free React theme palette switcher for Next.js and Tailwind, built with Motion. It renders a controlled radiogroup of named colour themes, each previewed with three decorative token swatches, and a shared spring indicator slides between options as you pick one. Selection is shown with a check badge and a moving indicator, not colour alone, so it stays readable at a glance.

The component is fully controlled: you own the selected id and it calls onValueChange once a theme is explicitly picked. It never touches documentElement, storage or any global theme state, so applying the choice, whether to a CSS variable, a context or a class on your own layout, stays entirely in your app. Two to six themes are supported, arrow keys follow the dock's orientation, and with reduced motion enabled the indicator jumps to place instantly with no scaling.

Install

Add the @te registry to your components.json once:

{
  "registries": {
    "@te": "https://templateempire.io/r/{name}.json"
  }
}

Then install the component:

npx shadcn@latest add @te/theme-palette-dock

Usage

"use client";

import { useState } from "react";
import { ThemePaletteDock } from "@/components/theme-palette-dock";

const themes = [
  {
    id: "midnight",
    name: "Midnight",
    swatches: [
      "var(--color-neutral-950)",
      "var(--color-brand-500)",
      "var(--color-neutral-50)",
    ],
  },
  {
    id: "daylight",
    name: "Daylight",
    swatches: [
      "var(--color-neutral-50)",
      "var(--color-brand-600)",
      "var(--color-neutral-950)",
    ],
  },
];

export default function Page() {
  const [theme, setTheme] = useState("midnight");

  return (
    <ThemePaletteDock
      themes={themes}
      value={theme}
      onValueChange={setTheme}
    />
  );
}

Props

PropTypeDefaultDescription
themesReadonlyArray<{ id: string; name: string; swatches: readonly [string, string, string] }>requiredTwo to six named themes. Malformed entries and duplicate ids are dropped safely.
valuestringrequiredControlled selected theme id. An id absent from themes falls back to the first valid theme visually, without calling onValueChange.
onValueChange(id: string) => voidrequiredCalled with a theme's id once it is explicitly selected.
orientation"horizontal" | "vertical""horizontal"Dock orientation. Arrow keys follow it.
swatchSizenumber36Option button size in CSS pixels, clamped to 32 through 56.
indicatorStiffnessnumber380Shared selection-indicator spring stiffness, clamped to 140 through 600.
previewMode"swatches" | "cards""swatches"Decorative sample treatment inside each option. Changes the sample only, never button geometry.
radiusnumber18Dock radius in CSS pixels, clamped to 0 through 36.
labelstring"Choose a colour theme"Accessible name for the radiogroup.
classNamestringundefinedAdditional classes merged onto the dock.

Customisation

Theme Palette Dock exposes three tokens: --palette-dock-panel for the dock surface (defaults to --card), --palette-dock-accent for the selection indicator and focus ring (defaults to --primary), and --palette-dock-divider for the dock border (defaults to --border). Override any of them from an ancestor to retheme the dock without touching its code:

<ThemePaletteDock
  themes={themes}
  value={theme}
  onValueChange={setTheme}
  swatchSize={44}
  previewMode="cards"
/>

/* retheme the dock itself */
:root {
  --palette-dock-panel: var(--surface);
  --palette-dock-accent: var(--primary-accent);
  --palette-dock-divider: var(--border);
}

Frequently asked questions

Is Theme Palette Dock free for commercial use?
Yes. Theme Palette Dock is MIT licensed, so you can use it in personal and commercial projects without attribution.
Does Theme Palette Dock apply the theme itself?
No. It is a fully controlled radiogroup: you pass the current value and it calls onValueChange with the id a visitor picks. It never mutates documentElement, storage or any global theme state, so wiring that id to CSS variables, a context provider or next-themes stays entirely in your app. It is a real radiogroup with roving tabindex, orientation-aware arrow keys, Home and End support, and a selected state that is stated programmatically and shown with a check badge and a moving indicator rather than colour alone.
What can the swatches prop safely contain?
Only a semantic colour token: a bare custom property name such as --primary, or a var(--token-name) reference to one. Anything else, including hex codes, oklch() or other raw CSS, is rejected and swapped for the dock's own divider token before it ever reaches a style declaration, so a theme fixture can never inject arbitrary CSS.

Related components

Want the full page this belongs to?Template Empire builds complete Next.js templates with this motion system throughout. Browse the paid templates that pair with these free components.Explore Template Empire templates