Color Picker
ProA 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.
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
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.
import {
ColorPicker,
ColorPickerModes,
ColorPickerGradient,
ColorPickerImage,
ColorPickerFormat,
ColorPickerEyeDropper,
ColorPickerArea,
ColorPickerControls,
ColorPickerFields,
ColorPickerSwatches,
ColorPickerPopover,
ColorPickerTrigger,
ColorPickerTriggerSwatch,
ColorPickerContent,
} from "@/components/ui/color-picker"
<ColorPickerPopover>
<ColorPickerTrigger asChild>
<ColorPickerTriggerSwatch />
</ColorPickerTrigger>
<ColorPickerContent>
<ColorPicker>
<ColorPickerModes />
<ColorPickerGradient />
<ColorPickerImage />
<ColorPickerFormat />
<ColorPickerEyeDropper />
<ColorPickerArea />
<ColorPickerControls />
<ColorPickerFields />
<ColorPickerSwatches />
</ColorPicker>
</ColorPickerContent>
</ColorPickerPopover>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.
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
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
The root: owns the color, format, mode, gradient and image state, and renders the standard stacked layout unless you pass children. Forwards native <div> props except onChange and defaultValue; every part accepts className, merged last.
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | - | Controlled solid color as hex, #rrggbb or #rrggbbaa. |
| defaultValue | string | "#3b82f6" | Initial solid color when uncontrolled. |
| onValueChange | (hex: string) => void | - | Fires with the new hex on every solid-color change; it carries the aa byte once alpha drops below 100%. |
| size | "sm" | "md" | "md" | Panel footprint: md is the full 324px panel, sm the 216px one with no fields row. |
| density | "comfortable" | "compact" | - | Only picks the default size (compact gives sm) when passed explicitly; it does not read an ambient DensityProvider, and size always wins. |
| surface | "card" | "plain" | "card" | card paints its own border, radius and elevation; plain inherits whatever holds it. |
| format | "hex" | "hsl" | "rgb" | - | Controlled channel format of the fields row; display only, the color never changes. |
| defaultFormat | "hex" | "hsl" | "rgb" | "hex" | Initial channel format when uncontrolled. |
| onFormatChange | (format: "hex" | "hsl" | "rgb") => void | - | Fires when the channel format changes. |
| showAlpha | boolean | true | Show the alpha rail and its percentage field. |
| showInput | boolean | true at size="md" | Show the format switcher and the fields row, which are one unit; off by default at sm. |
| showPresets | boolean | true | Show the preset swatch palette. |
| presets | string[] | defaultColorPresets | Hex colors for the preset palette; the default is 16 colors from Koala's ramps. |
| modes | ("solid" | "gradient" | "image")[] | ["solid"] | Fill kinds to offer; two or more add the fill-type tile row at the top. |
| mode | "solid" | "gradient" | "image" | - | Controlled active fill mode. |
| defaultMode | "solid" | "gradient" | "image" | modes[0] | Initial fill mode when uncontrolled. |
| onModeChange | (mode: "solid" | "gradient" | "image") => void | - | Fires when the active fill mode changes. |
| onFillChange | (fill: string) => void | - | Fires in every mode with one CSS value: a hex, a linear-gradient() or radial-gradient(), or url("…"). |
| defaultGradient | GradientValue | - | Initial gradient, { type, angle, stops }; defaults to a 90° indigo to pink linear gradient. |
| defaultImage | ColorPickerImageValue | { src: "", fit: "cover" } | Initial image fill when uncontrolled. |
| children | React.ReactNode | - | Recompose the parts yourself; omit for the standard layout. |
| CSS variable | Description |
|---|---|
| --surface | Set to var(--popover) in the card surface, read by the nested fields and swatch rings. |
ColorPickerFormat
The HEX / HSL / RGB switcher, a segmented ToggleGroup with radio semantics that always keeps one format chosen. Takes only className.
ColorPickerArea
The 2D saturation and brightness square, a focusable role="slider"; arrow keys nudge by 1, or 10 with Shift. Takes only className.
ColorPickerControls
The hue rail with the alpha rail under it when showAlpha is on: ColorPickerHueSlider plus ColorPickerAlphaSlider. Takes only className.
ColorPickerHueSlider
The hue rail on its own, a Radix Slider from 0 to 360. Takes only className.
ColorPickerAlphaSlider
The alpha rail on its own, a Radix Slider from 0 to 100 over a checkerboard. Takes only className.
ColorPickerFields
The channel row for the active format: one hex box, or three numeric fields for HSL and RGB, plus an alpha percentage when showAlpha is on. Takes only className.
ColorPickerHexInput
The hex field on its own, with a # prefix; it commits as soon as the text parses. Takes only className.
ColorPickerEyeDropper
An icon button that samples a pixel from the screen; renders nothing where the browser has no EyeDropper API. Takes only className.
ColorPickerPreview
A 36px chip of the current color over a checkerboard, for layouts that want one. Takes only className.
ColorPickerSwatches
The preset palette: a labelled section fenced off by a hairline, holding a wrapping row of 24px chips.
| Prop | Type | Default | Description |
|---|---|---|---|
| presets | string[] | defaultColorPresets | Hex colors to show; the default layout passes the root's presets through. |
| label | React.ReactNode | "Colors" | Section heading; null drops it. |
| Data attribute | Description |
|---|---|
| data-selected | "true" on the chip matching the current color, which also carries aria-pressed. |
ColorPickerModes
The fill-type row of live-preview tiles (Solid, Linear, Radial, Image), filtered by modes. Renders nothing with fewer than two tiles. Takes only className.
ColorPickerGradient
The gradient editor: a stop track (click to add, drag or arrow keys to move, Delete to remove while more than two stops remain) and an angle rail for linear gradients. Takes only className.
| Data attribute | Description |
|---|---|
| data-active | "true" on the stop button being edited. |
ColorPickerImage
The image fill: an upload button or live preview, a URL field, and a Cover / Contain toggle once an image is set. Takes only className.
ColorPickerPopover
Radix Popover.Root; renders no DOM.
| Prop | Type | Default | Description |
|---|---|---|---|
| open | boolean | - | Controlled open state. |
| defaultOpen | boolean | false | Initial open state when uncontrolled. |
| onOpenChange | (open: boolean) => void | - | Called when the popover opens or closes. |
| modal | boolean | false | Trap focus and block outside interaction while open. |
ColorPickerTrigger
Radix Popover.Trigger, unstyled; renders a <button> and forwards all its props.
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean | false | Merge onto your own control, usually a ColorPickerTriggerSwatch. |
| Data attribute | Description |
|---|---|
| data-state | "open" or "closed". |
ColorPickerTriggerSwatch
A ready-made 36px swatch button showing a color over a checkerboard. Forwards native <button> props except value.
| Prop | Type | Default | Description |
|---|---|---|---|
| value* | string | - | The color to show; pass the same value you give the picker. |
ColorPickerContent
Radix Popover.Content portalled to the body; it only positions and animates, since the ColorPicker inside paints the card. Forwards all its props.
| Prop | Type | Default | Description |
|---|---|---|---|
| side | "top" | "right" | "bottom" | "left" | "bottom" | Preferred side of the trigger; the panel flips when there is no room. |
| align | "start" | "center" | "end" | "start" | Alignment against the trigger. |
| sideOffset | number | 8 | Gap in pixels between the trigger and the panel. |
| Data attribute | Description |
|---|---|
| data-state | "open" or "closed". |
| data-side | The side the panel actually opened on, after collisions. |
| data-align | "start", "center" or "end". |