ColorPicker

Pick a colour by dragging — an area, a hue track, an optional opacity track, a hex field and swatches.

Open Source

Installation

ColorPicker ships with the library — install it once, then import:

import { ColorPicker } from "@delacour/react-native-ui/color-picker";

Or copy the source into your project, to own and edit it:

bunx delacour@latest add color-picker

It needs the sheet engine, even inline

The color-picker subpath imports BottomSheet for its sheet presentation, and Metro resolves static imports whichever branch renders — so install @delacour/react-native-bottom-sheet even for an inline picker, the way chart needs its engine. For presentation="sheet", mount BottomSheetProvider once in the app.

Usage

app/index.tsx
import { ColorPicker } from "@/components/ui/color-picker";
import { useState } from "react";
import { View } from "react-native";

export default function Example() {
  const [accent, setAccent] = useState("#f59e0b");

  return (
    <View className="flex-1 justify-center p-4">
      <ColorPicker defaultValue={accent} onValueCommit={setAccent} />
    </View>
  );
}

A bare <ColorPicker /> draws its default anatomy: the saturation and brightness area, the hue track, the opacity track when hasAlpha, and the hex field.

In a sheet

presentation="sheet" puts the surface in a BottomSheet behind a trigger row — a preview, the hex and a chevron, drawn on Input's own box so it sits level with every field beside it.

In a sheet
<Field>
  <Field.Label>Accent</Field.Label>
  <ColorPicker defaultValue="#f59e0b" hasAlpha presentation="sheet" onValueCommit={save}>
    <ColorPicker.Trigger />
    <ColorPicker.Content>
      <ColorPicker.Area />
      <ColorPicker.Hue />
      <ColorPicker.Alpha />
      <ColorPicker.Input />
      <ColorPicker.Swatches colors={PRESETS} />
    </ColorPicker.Content>
  </ColorPicker>
</Field>

Inside a Field the picker takes its disabled and invalid states, prefixes each part's accessible name with the label, and opens when the label is tapped — or, inline, focuses the hex field.

Value and formats

value and defaultValue accept #rgb, #rgba, #rrggbb, #rrggbbaa, rgb(), rgba(), hsl() and hsla(). format picks what the callbacks report:

formatReported
"hex" (default)#rrggbb, or #rrggbbaa below full opacity
"rgb"rgb(r, g, b), or rgba(r, g, b, a)
"hsl"hsl(h, s%, l%), or hsla(h, s%, l%, a)

onValueChange fires on every frame of a drag; onValueCommit fires once the change is settled — a drag released, the field submitted, a swatch tapped. A network write belongs in the second.

<ColorPicker format="hsl" value={value} onValueChange={setValue} onValueCommit={save} />

A controlled parent can store whatever onValueChange hands it: the picker recognises its own echo and keeps the hue of a grey or black rather than snapping it to red.

Alpha

hasAlpha adds the opacity track — the colour fading to a checkerboard, with the same RGB the whole way. Without it the alpha is pinned at one on every path, and ColorPicker.Alpha renders nothing.

With alpha
<ColorPicker defaultValue="#3b82f6b3" hasAlpha>
  <ColorPicker.Content>
    <ColorPicker.Area />
    <ColorPicker.Hue />
    <ColorPicker.Alpha />
    <View className="flex-row items-center gap-3">
      <ColorPicker.Preview />
      <ColorPicker.Input className="flex-1" />
    </View>
  </ColorPicker.Content>
</ColorPicker>

Swatches

Presets in any notation. Tapping one sets the colour and commits; the current colour's swatch is ringed, compared as hex.

Swatches
<ColorPicker.Swatches colors={["#ef4444", "#f59e0b", "#22c55e", "#3b82f6"]} />

Sizes and variants

size is Input's sm, md or lg: the area is 160, 200 or 240 points tall, and the tracks, thumbs, swatches and field step with it. variant (primary, secondary or etched) reaches the trigger row and the hex field — never the gradients.

Sizes
Sizes

States

isDisabled fades the picker and blocks every input path. isReadOnly draws the colour normally and ignores every drag, tap and keystroke. isInvalid turns the trigger and the field destructive. Text in the hex field that is not a colour leaves the value alone and marks the field invalid until it parses.

Accessibility

  • The area is adjustable: swipe up and down to step saturation, and use the Brighter and Darker actions for brightness. It reads "Saturation 60 percent, brightness 80 percent".
  • The hue track reads "Hue 210 degrees, blue" and steps ten degrees, wrapping; the opacity track steps five percent.
  • Swatches are a radio group, labelled by hex.
  • The value is announced when a change is committed, never per frame.

API Reference

Anatomy

PartWhat it is
ColorPickerOwns the colour, the format and the axes
ColorPicker.TriggerThe row that opens the sheet. Sheet presentation only
ColorPicker.ContentThe surface — a column inline, the sheet's panel otherwise
ColorPicker.AreaSaturation across, brightness down
ColorPicker.HueThe hue track
ColorPicker.AlphaThe opacity track. Nothing without hasAlpha
ColorPicker.PreviewThe current colour over a checkerboard
ColorPicker.InputThe hex field
ColorPicker.SwatchesA row of presets
ColorPicker.SwatchOne preset

ColorPicker

Prop

Type

ColorPicker.Area, .Hue, .Alpha

Prop

Type

ColorPicker.Input

Every Input prop except value, onChangeText, size, variant and isDisabled, which are the picker's. isInvalid overrides the picker's own state.

ColorPicker.Swatches

Prop

Type

ColorPicker.Swatch

Prop

Type

useColorPicker

Returns the settled state — hsva, rgba, value, hex, the axes, and setHsva(next, { commit }) — for a custom part that has to match the picker it sits in. Throws outside a ColorPicker. The settled state catches up when a drag ends; it does not follow the finger frame by frame.

On this page