Skip to content

ComponentsForms

Color Picker

A ready-made swatch button that opens a saturation/brightness area, hue slider, hex field and presets in a popover, plus every building block (ColorArea, ColorSlider, ColorField, ColorSwatch, ColorSwatchPicker) to compose your own inline or popover editors.

React AriaSource

Installation

pnpm dlx shadcn@latest add @desyne/color-picker

The CLI installs dependencies and any other components this one uses.

Usage

tsx
import { ColorPicker } from "@/components/ui/color-picker";
tsx
<ColorPicker label="Accent" defaultValue="#6366f1" swatches={["#ef4444", "#22c55e"]} />

ColorPicker is a complete control. For a custom popover, pass the parts as children. For an inline editor, wrap the parts in React Aria's ColorPicker, which keeps one shared color in sync between them:

tsx
import { ColorPicker as ColorPickerState } from "react-aria-components";
import { ColorArea, ColorField, ColorSlider } from "@/components/ui/color-picker";

<ColorPickerState defaultValue="#6366f1">
  <ColorArea colorSpace="hsb" xChannel="saturation" yChannel="brightness" />
  <ColorSlider label="Hue" colorSpace="hsb" channel="hue" />
  <ColorField label="Hex" />
</ColorPickerState>;

Values can be passed as any CSS color string ("#6366f1", "rgb(…)", "hsl(…)") or a Color object from parseColor. onChange always receives a Color; call color.toString("hex") (or "rgb", "hsl", "css"…) to serialize it.

When to use

  • Color Picker — any color, when users need precise control or a custom brand color.
  • ColorSwatchPicker on its own — a fixed palette, such as label or calendar colors. Faster and keeps colors consistent.
  • ColorField — users who already know the hex value, like designers pasting from a design tool.
  • ColorSwatch — displaying a color that isn't editable.

Anatomy

tsx
<ColorPicker>                 {/* trigger button + popover dialog */}
  <ColorArea />               {/* 2D saturation × brightness, with a ColorThumb */}
  <ColorSlider />             {/* one channel: hue, alpha, red… */}
  <ColorField />              {/* hex or single-channel text input */}
  <ColorSwatchPicker>         {/* preset list */}
    <ColorSwatchPickerItem /> {/* one preset, renders a ColorSwatch */}
  </ColorSwatchPicker>
</ColorPicker>
PartRendersNotes
ColorPicker<button> + popover role="dialog"Outline button with a swatch and label. The dialog holds the default editor, or your children. With name, also a hidden <input> after the button.
ColorArea<div> + two hidden <input type="range">Drags two channels at once. Defaults to 192px square; add w-full to fill its container.
ColorSlider<div role="group">Label and value output (when label is set) above a 12px gradient track.
ColorThumb<div>The white ring handle used by ColorArea and ColorSlider. Exported for custom compositions.
ColorField<div> + <input>Monospace text input with optional label, description and error.
ColorSwatch<div role="img">A static color preview, labelled with a human-readable color name.
ColorSwatchPicker<div role="listbox">A wrapping row of selectable presets.
ColorSwatchPickerItem<div role="option">One preset; shows a ring when hovered, focused or selected.

Examples

Controlled

Pass a Color as value and update it in onChange. Keep the Color object in state and serialize it only when you need a string.

hex
#0EA5E9
rgb
rgb(14, 165, 233)
hsl
hsl(198.63, 88.66%, 48.43%)

Custom popover content

children replace the default popover content. Here an alpha slider is added so users can pick translucent colors; the parts inside still share the picker's value.

Inline editor

The same parts without a popover, wrapped in React Aria's ColorPicker. Use it in sidebars and design tools where the editor is always visible.

200°
100%

Channel sliders

ColorSlider edits one channel in a colorSpace: rgb (red, green, blue), hsl (hue, saturation, lightness), hsb (hue, saturation, brightness), plus alpha. The track previews the gradient for that channel.

234
88
12
48%

Channel fields

A ColorField with a channel edits a single number instead of the hex string, so users can type exact RGB or HSL values.

Color field

A standalone hex input. The value is parsed when the user presses Enter or leaves the field, and arrow keys step it. isInvalid with errorMessage shows your own validation, such as a contrast check.

Hex, with or without the #. Applied when you press Enter or leave the field.
Too little contrast against a white background (1.3:1).

Swatch picker

ColorSwatchPicker is a single-select list of presets with its own value, defaultValue and onChange. Give it an aria-label describing what the color is for.

Swatches

ColorSwatch displays a color. It's announced by its color name (e.g. "dark vibrant blue"); pass colorName to use your own palette's name instead.

  • Ink#0f172a
  • Ocean#0369a1
  • Moss#4d7c0f
  • Clay#c2410c
  • Rose#be123c
  • Frost#e0f2fe

Disabled and read-only

isDisabled on ColorPicker disables the trigger, keeps the popover closed and leaves its value out of form submissions. On ColorArea and ColorSlider it dims them and removes them from the tab order. ColorField supports both isDisabled and isReadOnly.

174.67°

Form submission

ColorField takes a name and submits its hex value. ColorPicker takes a name too: it renders a hidden input next to the trigger, inside your form, so it submits even though the editor lives in a popover. The value is an uppercase #RRGGBB hex string by default; valueFormat picks another format, such as "hexa" to keep alpha or "rgb" / "hsl" / "css". form associates the input with a <form> by id.

Recipes

Theme editor

A brand color applied live to buttons and badges through the --tone and --tone-fg variables, with a readable foreground picked automatically.

Brand color

Used for buttons, links and highlights.

NewAccent text

Label editor

A name field and a palette of presets, with a live preview of the resulting label.

New label
Needs design

Gradient builder

Two pickers and a Slider for the angle, producing copyable CSS.

135 deg
background: linear-gradient(135deg, #F97316, #DB2777);

Accessibility

  • The ColorPicker trigger is a button; the popover is a labelled dialog (named by label, then aria-label, or "Color picker") that traps focus, closes on Esc and returns focus to the trigger.
  • The default editor's preset list is labelled "Presets".
  • ColorArea exposes two visually hidden range inputs, one per channel, announced as a two-dimensional slider. Values are announced with the color's name, like "vibrant purple".
  • ColorSlider is a native range input announced with its channel name and value. Without a label it's labelled by the channel name; pass aria-label to override it.
  • ColorField without a visible label needs an aria-label (the default popover uses "Hex").
  • ColorSwatchPicker is a listbox: one tab stop, arrow keys to move, and each option announced by color name.
  • Don't rely on color alone to convey meaning in what users build with the picked color; check contrast when a color is used for text.

Keyboard

KeyAction
TabMoves between the trigger, area, sliders, fields and swatches
Enter / SpaceOn the trigger: opens the popover. On a swatch: selects it.
← / → / ↑ / ↓In the area: adjusts the x / y channel. In a slider: adjusts the channel. In the swatch picker: moves focus.
Shift + arrowsIn the area or a slider: adjusts by a larger step
Page Up / Page DownAdjusts the area's y channel, or a slider, by a larger step
Home / EndAdjusts the area's x channel by a larger step; sets a slider to its min / max
↑ / ↓ in a fieldSteps the value
EscCloses the popover

Styling

Data attributes

PartAttributes
ColorAreadata-disabled
ColorSliderdata-disabled, data-orientation
ColorThumbdata-dragging, data-hovered, data-focused, data-focus-visible, data-disabled
ColorFielddata-disabled, data-invalid, data-readonly, data-required, data-channel
ColorSwatchPickerItemdata-selected, data-hovered, data-pressed, data-focused, data-focus-visible, data-disabled

The thumb grows while dragging or keyboard-focused (data-dragging:size-5.5, data-focus-visible:size-5.5).

Slots

data-slotElement
color-area, color-slider, color-thumbArea, slider and their thumb
color-fieldField root (input on the input itself)
color-swatchEvery swatch, including the trigger's and each preset's
color-swatch-picker, color-swatch-picker-itemPreset list and items
label, description, field-errorField and slider text
color-picker-triggerThe ColorPicker trigger button (it also has button's styles)
popoverThe ColorPicker popover

Sizing

ColorArea is size-48 by default; className="w-full" makes it fill the popover or its container. ColorSwatch is size-7; ColorSwatchPickerItem always renders a size-7 swatch. The popover is 13.5rem wide.

Render props

Every part's className accepts a function of its state, e.g. the swatch picker item's isSelected or the thumb's isDragging:

tsx
<ColorSwatchPickerItem
  color="#2563eb"
  className={({ isSelected }) => (isSelected ? "scale-110" : "")}
/>

API Reference

ColorPicker

Prop

Type

These are the props ColorPicker accepts. React Aria's ColorPicker is the state provider underneath.

ColorArea

Prop

Type

Also accepts every prop of React Aria's ColorArea.

ColorSlider

Prop

Type

Also accepts every prop of React Aria's ColorSlider. Styled for horizontal sliders only.

ColorField

Prop

Type

Also accepts every prop of React Aria's ColorField.

ColorSwatch

Prop

Type

ColorSwatchPicker

Prop

Type

ColorSwatchPickerItem

Prop

Type

See React Aria's ColorSwatchPicker and ColorSwatch.

ColorThumb

Accepts React Aria's ColorThumb props (className, style, hover events). ColorArea and ColorSlider render one for you.

  • Slider — the same interaction for plain numbers.
  • Popover — the overlay the picker opens in.
  • Text Field — ColorField shares its input styles.