Skip to content

Build a Color Swatch Group

Let's build a keyboard-navigable color swatch group for selecting colors from a palette.

Here's what we'll end up with:

Click to view the full code
tsx
import { useState } from "react";
import "internationalized-color/css";
import { ColorSwatchGroup } from "@urcolor/react";

const colors = [
  "hsl(210, 80%, 50%)",
  "hsl(350, 90%, 60%)",
  "hsl(120, 60%, 45%)",
  "hsl(45, 100%, 55%)",
  "hsl(280, 70%, 55%)",
  "hsl(15, 85%, 55%)",
];

export default function ColorSwatchGroupGuide() {
  const [selected, setSelected] = useState<string[]>([colors[0]!]);

  return (
    <div className="flex flex-col gap-4">
      <ColorSwatchGroup.Root
        value={selected}
        onValueChange={setSelected}
        type="single"
        className="flex items-center gap-2"
      >
        {colors.map((color) => (
          <ColorSwatchGroup.Item
            key={color}
            value={color}
            className="
              flex size-10 cursor-pointer items-center justify-center rounded-lg
              outline-none
            "
          >
            <svg
              className={`size-5 text-white drop-shadow-[0_1px_2px_rgba(0,0,0,0.5)] transition-opacity duration-150 ${selected.includes(color) ? "opacity-100" : "opacity-0"}`}
              xmlns="http://www.w3.org/2000/svg"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              strokeWidth={2}
              strokeLinecap="round"
              strokeLinejoin="round"
            >
              <polyline points="20 6 9 17 4 12" />
            </svg>
          </ColorSwatchGroup.Item>
        ))}
      </ColorSwatchGroup.Root>
      <p className="text-sm text-[var(--vp-c-text-2)]">
        Selected: <code>{selected[0] ?? "none"}</code>
      </p>
    </div>
  );
}

Step 1: Set up state

Define your color palette and selection state.

tsx
import { useState } from "react"; 

function MyGroup() {
  const colors = [ 
    "hsl(210, 80%, 50%)", 
    "hsl(350, 90%, 60%)", 
    "hsl(120, 60%, 45%)", 
    "hsl(45, 100%, 55%)", 
  ]; 

  const [selected, setSelected] = useState<string[]>([colors[0]!]); 
}

The selection is always an array of strings — even for single selection mode.

Step 2: Add the group root

ColorSwatchGroup.Root manages selection state and keyboard navigation.

tsx
import { useState } from "react";
import { ColorSwatchGroup } from "@urcolor/react"; 

function MyGroup() {
  const colors = [
    "hsl(210, 80%, 50%)",
    "hsl(350, 90%, 60%)",
    "hsl(120, 60%, 45%)",
    "hsl(45, 100%, 55%)",
  ];
  const [selected, setSelected] = useState<string[]>([colors[0]!]);

  return (
    <ColorSwatchGroup.Root
      value={selected}
      onValueChange={setSelected}
      type="single"
      className="flex items-center gap-2"
    >
      {/* items go here */}
    </ColorSwatchGroup.Root>
  );
}
  • type="single" — only one color can be selected at a time
  • type="multiple" — multiple colors can be selected

Step 3: Add swatch items

ColorSwatchGroup.Item renders each selectable color. The value prop is both the selection value and the displayed color.

tsx
import { useState } from "react";
import { ColorSwatchGroup } from "@urcolor/react";

function MyGroup() {
  const colors = [
    "hsl(210, 80%, 50%)",
    "hsl(350, 90%, 60%)",
    "hsl(120, 60%, 45%)",
    "hsl(45, 100%, 55%)",
  ];
  const [selected, setSelected] = useState<string[]>([colors[0]!]);

  return (
    <ColorSwatchGroup.Root
      value={selected}
      onValueChange={setSelected}
      type="single"
      className="flex items-center gap-2"
    >
      {colors.map((color) => (
        <ColorSwatchGroup.Item
          key={color}
          value={color}
          className="
            size-10 cursor-pointer rounded-lg
            flex items-center justify-center outline-none
          "
        />
      ))}
    </ColorSwatchGroup.Root>
  );
}

TIP

All components are completely unstyled — the classes above are just an example using Tailwind CSS. Use any styling approach you prefer.

Multiple selection

Change type to "multiple" to allow selecting more than one color:

tsx
<ColorSwatchGroup.Root
  value={selected}
  onValueChange={setSelected}
  type="multiple"
  className="flex items-center gap-2"
>
  {colors.map((color) => (
    <ColorSwatchGroup.Item key={color} value={color} className="..." />
  ))}
</ColorSwatchGroup.Root>

Keyboard navigation

The group supports full keyboard navigation out of the box:

  • Arrow keys — move focus between swatches
  • Space / Enter — toggle selection
  • Home / End — jump to first or last swatch

Listening to changes

Use onValueChange to react to selection changes:

tsx
import { ColorSwatchGroup } from "@urcolor/react";

const onSelectionChange = (value: string[]) => {
  console.log("selected:", value);
};

<ColorSwatchGroup.Root
  value={selected}
  onValueChange={onSelectionChange}
  type="single"
>
  {/* ... */}
</ColorSwatchGroup.Root>