Color Picker

Pro

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.

Colors

#67aa38

Installation#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Activate your Pro license (once per machine)npx koalaui-cli@latest login <key># Add this component (its dependencies come along)npx koalaui-cli@latest add color-picker

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.

This example is part of Koala UI Pro

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>

Colors

Playground#

Flip the knobs to see the panel and its snippet change together.

Colors

Playground
Props

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.

Parts

The format switcher and its channel fields.

The transparency rail and its field.

The labelled preset palette.

This example is part of Koala UI Pro

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.

defaultFormat="hex"
defaultFormat="hsl"
defaultFormat="rgb"

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.

Colors

md · 324px

Colors

sm · 216px

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.

showAlpha (default)#a855f7cc
showAlpha={false}#f0b100

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.

Colors

a custom palette#f97316
label={null}

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.

surface="card" (default)
Brand color

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().

90°

Colors

linear-gradient(90deg, #6366f1 0%, #ec4899 100%)

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.

#22c55e

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.

Brand palette

swatches only#8b5cf6
square and rails only

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.

PropTypeDefaultDescription
valuestring-Controlled solid color as hex, #rrggbb or #rrggbbaa.
defaultValuestring"#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.
showAlphabooleantrueShow the alpha rail and its percentage field.
showInputbooleantrue at size="md"Show the format switcher and the fields row, which are one unit; off by default at sm.
showPresetsbooleantrueShow the preset swatch palette.
presetsstring[]defaultColorPresetsHex 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("…").
defaultGradientGradientValue-Initial gradient, { type, angle, stops }; defaults to a 90° indigo to pink linear gradient.
defaultImageColorPickerImageValue{ src: "", fit: "cover" }Initial image fill when uncontrolled.
childrenReact.ReactNode-Recompose the parts yourself; omit for the standard layout.
CSS variableDescription
--surfaceSet 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.

PropTypeDefaultDescription
presetsstring[]defaultColorPresetsHex colors to show; the default layout passes the root's presets through.
labelReact.ReactNode"Colors"Section heading; null drops it.
Data attributeDescription
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 attributeDescription
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.

PropTypeDefaultDescription
openboolean-Controlled open state.
defaultOpenbooleanfalseInitial open state when uncontrolled.
onOpenChange(open: boolean) => void-Called when the popover opens or closes.
modalbooleanfalseTrap focus and block outside interaction while open.

ColorPickerTrigger

Radix Popover.Trigger, unstyled; renders a <button> and forwards all its props.

PropTypeDefaultDescription
asChildbooleanfalseMerge onto your own control, usually a ColorPickerTriggerSwatch.
Data attributeDescription
data-state"open" or "closed".

ColorPickerTriggerSwatch

A ready-made 36px swatch button showing a color over a checkerboard. Forwards native <button> props except value.

PropTypeDefaultDescription
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.

PropTypeDefaultDescription
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.
sideOffsetnumber8Gap in pixels between the trigger and the panel.
Data attributeDescription
data-state"open" or "closed".
data-sideThe side the panel actually opened on, after collisions.
data-align"start", "center" or "end".

FAQ#

Reach for ColorPicker when a user needs to choose an arbitrary color: it gives you the 2D saturation/brightness square, hue and alpha rails, the HEX/HSL/RGB fields, and an eyedropper. If you only need a fixed set of brand colors, render ColorPickerSwatches on its own with a presets array, and if you just need a 1D value pick a plain Slider instead.