Siteplane UI

Color Swatch Picker

Select a named color or image from an application-owned palette.

Markdown

Shown on the label in lists and filters.

"use client";

import { ColorSwatchPicker } from "@/siteplane/components/color-swatch-picker";
import {
  Field,
  FieldDescription,
  FieldLabel,
} from "@/siteplane/components/field";

const labelColors = [
  { value: "indigo", label: "Indigo", color: "#6366f1" },
  { value: "sky", label: "Sky", color: "#0ea5e9" },
  { value: "green", label: "Green", color: "#22c55e" },
  { value: "amber", label: "Amber", color: "#f59e0b" },
  { value: "rose", label: "Rose", color: "#f43f5e" },
  { value: "slate", label: "Slate", color: "#64748b" },
];

export default function Example() {
  return (
    <Field className="w-full max-w-sm">
      <FieldLabel>Label color</FieldLabel>
      <ColorSwatchPicker defaultValue="indigo" options={labelColors} />
      <FieldDescription>
        Shown on the label in lists and filters.
      </FieldDescription>
    </Field>
  );
}

Installation

pnpm dlx shadcn@4.18.0 add @siteplane/color-swatch-picker

Complete the one-time setup first. Registry JSON lists source files and dependencies.

For this page’s examples, also install:

pnpm dlx shadcn@4.18.0 add @siteplane/button @siteplane/form @siteplane/input

Field is installed with Input.

Usage

import { ColorSwatchPicker } from "@/siteplane/components/color-swatch-picker";

<ColorSwatchPicker
  aria-label="Color"
  options={[{ value: "green", label: "Green", color: "#22c55e" }]}
/>;

Controlled selection

The value is the option key, not its CSS color.

"use client";

import { ColorSwatchPicker } from "@/siteplane/components/color-swatch-picker";
import { Field, FieldLabel } from "@/siteplane/components/field";
import { useState } from "react";

const options = [
  { value: "indigo", label: "Indigo", color: "#6366f1" },
  { value: "green", label: "Green", color: "#22c55e" },
  { value: "rose", label: "Rose", color: "#f43f5e" },
  { value: "slate", label: "Slate", color: "#64748b", disabled: true },
];

export default function Example() {
  const [value, setValue] = useState("indigo");
  return (
    <Field className="w-full max-w-sm">
      <FieldLabel>Label color</FieldLabel>
      <ColorSwatchPicker
        options={options}
        value={value}
        onValueChange={setValue}
      />
    </Field>
  );
}

Selection ring

A selected swatch shows a check by default, drawn in like the Checkbox check and instantly under reduced motion. Set indicator="ring" to draw an accent ring around it instead, so the swatch keeps its full color.

"use client";

import { ColorSwatchPicker } from "@/siteplane/components/color-swatch-picker";
import { Field, FieldLabel } from "@/siteplane/components/field";

const labelColors = [
  { value: "indigo", label: "Indigo", color: "#6366f1" },
  { value: "sky", label: "Sky", color: "#0ea5e9" },
  { value: "green", label: "Green", color: "#22c55e" },
  { value: "amber", label: "Amber", color: "#f59e0b" },
  { value: "rose", label: "Rose", color: "#f43f5e" },
  { value: "slate", label: "Slate", color: "#64748b" },
];

export default function Example() {
  return (
    <Field className="w-full max-w-sm">
      <FieldLabel>Label color</FieldLabel>
      <ColorSwatchPicker
        defaultValue="indigo"
        indicator="ring"
        options={labelColors}
      />
    </Field>
  );
}

Image swatches

Give an option an image instead of a color and the picture fills the swatch. The color is optional and shows until the image has loaded. Use indicator="ring" so the selection doesn’t cover the picture.

Gets a notification when the draft is ready.

"use client";

import { ColorSwatchPicker } from "@/siteplane/components/color-swatch-picker";
import {
  Field,
  FieldDescription,
  FieldLabel,
} from "@/siteplane/components/field";

const reviewers = [
  {
    value: "mara",
    label: "Mara Ellis",
    image: "https://ui.siteplane.io/avatars/mara.webp",
  },
  {
    value: "ben",
    label: "Ben Carter",
    image: "https://ui.siteplane.io/avatars/ben.webp",
  },
  {
    value: "sofia",
    label: "Sofia Nguyen",
    image: "https://ui.siteplane.io/avatars/sofia.webp",
  },
];

export default function Example() {
  return (
    <Field className="w-full max-w-sm">
      <FieldLabel>Reviewer</FieldLabel>
      <ColorSwatchPicker
        defaultValue="mara"
        indicator="ring"
        options={reviewers}
      />
      <FieldDescription>
        Gets a notification when the draft is ready.
      </FieldDescription>
    </Field>
  );
}

Read-only and disabled

Read-only preserves selection and is announced as read-only; disabled also removes the submitted value.

"use client";

import { ColorSwatchPicker } from "@/siteplane/components/color-swatch-picker";
import { Field, FieldLabel } from "@/siteplane/components/field";

const options = [
  { value: "indigo", label: "Indigo", color: "#6366f1" },
  { value: "green", label: "Green", color: "#22c55e" },
  { value: "rose", label: "Rose", color: "#f43f5e" },
  { value: "slate", label: "Slate", color: "#64748b", disabled: true },
];

export default function Example() {
  return (
    <div className="grid w-full max-w-sm gap-4">
      <Field>
        <FieldLabel>Label color</FieldLabel>
        <ColorSwatchPicker options={options} defaultValue="green" readOnly />
      </Field>
      <Field>
        <FieldLabel>Project color</FieldLabel>
        <ColorSwatchPicker options={options} defaultValue="rose" disabled />
      </Field>
    </div>
  );
}

Form integration

Inside a Field, the picker submits the selected option’s value under the field’s name. With required, the form isn’t submitted until a color is chosen, and FieldError explains why.

"use client";

import { Button } from "@/siteplane/components/button";
import { ColorSwatchPicker } from "@/siteplane/components/color-swatch-picker";
import { Field, FieldError, FieldLabel } from "@/siteplane/components/field";
import { Form } from "@/siteplane/components/form";

const options = [
  { value: "indigo", label: "Indigo", color: "#6366f1" },
  { value: "green", label: "Green", color: "#22c55e" },
  { value: "rose", label: "Rose", color: "#f43f5e" },
  { value: "slate", label: "Slate", color: "#64748b", disabled: true },
];

export default function Example() {
  return (
    <Form
      className="grid w-full max-w-sm gap-4"
      onSubmit={(event) => {
        event.preventDefault();
      }}
    >
      <Field name="labelColor">
        <FieldLabel>Label color</FieldLabel>
        <ColorSwatchPicker options={options} required />
        <FieldError>Choose a label color.</FieldError>
      </Field>
      <Button type="submit">Save</Button>
    </Form>
  );
}

API reference

Component properties
PropTypeDefaultDescription
optionsreadonly ColorSwatchPickerOption[]RequiredEach option has label (accessible name), value (unique string), and optional disabled, plus a color (CSS color), an image (image URL), or both. An image fills the swatch; a color shows behind it until the image has loaded. imageSize and imagePosition (CSS background-size and background-position) show part of the image.
value / defaultValuestring—Controlled option key or uncontrolled initial key.
onValueChange(value: string) => void—Emits selected option key.
indicatorcheck | ringcheckHow the selected swatch shows it: a check inside the swatch, or an accent ring around it.
disabled / readOnly / requiredbooleanfalseDisable interaction, prevent changes, or require selection. readOnly and required are exposed on the listbox as aria-readonly and aria-required; a disabled Field also disables the picker.
name / formstring—Native form field name / form id; a Field name takes precedence.
inputRefReact.Ref<HTMLInputElement>—Reference to the visually hidden form input.
sizesm | md | lg | xlprovider / lgControl dimensions; swatches are 28, 32, 36 and 40 px.
className / style / aria-label / aria-labelledby / aria-describedbydiv props—Applied to the collection root; inside a Field, FieldLabel, FieldDescription and FieldError are connected automatically.

Exported types: ColorSwatchPickerProps, ColorSwatchPickerOption and ColorSwatchIndicator. These describe the contracts above.

Read the installed source.

Usage notes

Native reset restores uncontrolled selection. Inside a Field, FieldLabel names the group and clicking it focuses the selected swatch; outside a Field, pass aria-label or aria-labelledby. Each option label is announced. Keyboard navigation follows React Aria ColorSwatchPicker. Swatches are round by default; the radius of SiteplaneUIProvider squares them off (none) or rounds them only slightly (sm). To end a palette with a custom color, use the swatch trigger of Color Picker. Arbitrary children and native onChange are not part of this wrapper.