Color Picker
A full HSV color picker in one panel: a HEX/HSL/RGB switcher, a draggable saturation square, hue and alpha rails, channel fields, an eyedropper and a preset palette. The rails ride Radix Slider for keyboard and ARIA.
Installation#
Usage#
The panel paints its own card, so it drops straight onto a page. Pass value with onValueChange for a controlled hex string, or defaultValue to let it own its state.
import { ColorPicker } from "@/components/ui/color-picker"
export function Example() {
const [color, setColor] = useState("#67aa38")
return <ColorPicker value={color} onValueChange={setColor} />
}Anatomy#
Every piece of the panel is a named export, and the default layout is simply this composition. Pass children to write it out yourself: drop a part, reorder it, or wrap it. Nothing here is a showX flag that a part depends on.
Playground#
Flip the knobs to see the panel and its snippet change together.
Colors
The panel footprint. sm trades the fields row for a 216px width.
Whether the panel paints its own card, or inherits the surface it sits on.
The format switcher and its channel fields.
The transparency rail and its field.
The labelled preset palette.
<ColorPicker
value="#67aa38"
onValueChange={() => {}}
showInput
showAlpha
showPresets
/>Formats#
The switcher at the top of the panel chooses how the colour is written: one hex box, or three numeric channels for HSL and RGB. It never changes the colour. HSV stays the working model in every format, so cycling HEX → HSL → RGB round-trips losslessly, and onValueChange always emits hex. Set the initial format with defaultFormat, or drive it with format and onFormatChange.
Sizes#
Two footprints. md is the full 324px panel; sm is 216px and drops the fields row, which has no room at that width. Passing density="compact" picks sm as the default size, and size always wins over it. Unlike the form controls, the panel does not read an ambient DensityProvider: these are fixed panel widths, not height tiers, and inheriting compact from an app shell would silently take the fields away.
Alpha#
The transparency rail and its percentage field are on by default. The emitted hex carries the aa byte whenever alpha drops below 100%, so a fully opaque colour is still a plain six-digit hex. Pass showAlpha={false} for an opaque-only picker.
Presets#
The palette is a labelled section fenced off by a hairline. Pass your own presets array, and set label on ColorPickerSwatches to rename the heading or null to drop it. The chosen chip is marked with a ring in its own colour rather than a checkmark, which reads at 24px on light and dark swatches alike.
Surface#
By default the panel is a card: border, radius, elevation, and a --surface declaration so the fields and rings inside blend with it. Drop to surface="plain" when it already sits in something that draws the frame, such as a Card or a settings panel, so you never nest one card inside another.
Fill modes#
Pass modes with two or more entries to add a Figma-style fill-type row at the top: a tile per fill kind (Solid, each gradient geometry, Image), each a live preview. The gradient editor gives you a draggable stop track (click to add a stop, drag to move it, Delete to remove) and an angle rail for linear gradients; the image mode takes an upload or a URL with a cover/contain fit. Use onFillChange to receive one CSS value across every mode: a hex, a linear-gradient(), or a url().
In a popover#
Wrap a ColorPickerTriggerSwatch in ColorPickerTrigger for the classic field affordance: a swatch of the current color that opens the panel on click. ColorPickerContent only positions and animates: the card you see is the picker's own, so there is exactly one frame either way.
Composition#
Because the parts are droppable, the same component covers the whole range from a full panel down to a bare swatch grid or a square with two rails. Render only what the task needs; the state, the keyboard handling, and the colour maths come along either way.
API reference#
ColorPicker takes value, defaultValue, onValueChange, size, surface, density, format, defaultFormat, onFormatChange, showAlpha, showInput, showPresets, presets, and the fill-mode props modes, mode, defaultMode, onModeChange, onFillChange, defaultGradient, defaultImage.
The parts are ColorPickerFormat, ColorPickerArea, ColorPickerControls (which composes ColorPickerHueSlider and ColorPickerAlphaSlider), ColorPickerFields, ColorPickerHexInput, ColorPickerEyeDropper, ColorPickerPreview, ColorPickerSwatches, ColorPickerModes, ColorPickerGradient, and ColorPickerImage, plus the popover trio ColorPickerPopover, ColorPickerTrigger, ColorPickerContent and the ready-made ColorPickerTriggerSwatch. The colour maths is exported too: hexToHsva, hsvaToHex, hsvaToRgba, rgbaToHsva, hsvaToHsla, hslaToHsva, and gradientToCss.