dotUI
dotUI
beta
  1. Components
  2. Colors
  3. Color Picker

Color Picker

A ColorPicker combines a swatch with a customizable popover for editing a color.

import { ColorPicker } from "@/lib/components/core/default/color-picker";

function Demo() {
  return <ColorPicker defaultValue="#5100FF" />;
}

Installation

Install the following dependencies:

npm install react-aria-components

Copy and paste the following code into your project.

"use client";

import React from "react";
import {
  getColorChannels,
  ColorPicker as AriaColorPicker,
  type ColorPickerProps as AriaColorPickerProps,
  type ColorSpace,
  composeRenderProps,
} from "react-aria-components";
import { cn } from "@/lib/utils/classes";
import { Button, type ButtonProps } from "./button";
import { ColorArea } from "./color-area";
import { ColorField } from "./color-field";
import { ColorSlider } from "./color-slider";
import { ColorSwatch } from "./color-swatch";
import { Dialog, DialogRoot } from "./dialog";
import { Item } from "./list-box";
import { Select } from "./select";

type ColorPickerProps = ColorPickerRootProps & Omit<ButtonProps, "children">;
const ColorPicker = ({
  slot,
  value,
  defaultValue,
  onChange,
  shape = "square",
  ...props
}: ColorPickerProps) => {
  return (
    <ColorPickerRoot slot={slot} value={value} defaultValue={defaultValue} onChange={onChange}>
      {composeRenderProps(props.children, (children) => (
        <DialogRoot>
          <Button shape={shape} {...props}>
            <ColorSwatch />
            {children}
          </Button>
          <Dialog type="popover" mobileType="drawer">
            <ColorEditor className="mx-auto" />
          </Dialog>
        </DialogRoot>
      ))}
    </ColorPickerRoot>
  );
};

type ColorPickerRootProps = AriaColorPickerProps;
const ColorPickerRoot = (props: ColorPickerRootProps) => {
  return <AriaColorPicker {...props} />;
};

type ColorEditorProps = React.HTMLAttributes<HTMLDivElement>;
const ColorEditor = ({ className, ...props }: ColorEditorProps) => {
  const [space, setSpace] = React.useState<ColorSpace | "hex">("hex");
  return (
    <div className={cn("mx-auto flex flex-col gap-2", className)} {...props}>
      <div className="flex gap-2">
        <ColorArea colorSpace="hsb" xChannel="saturation" yChannel="brightness" />
        <ColorSlider orientation="vertical" colorSpace="hsb" channel="hue" showValueLabel={false} />
        <ColorSlider
          orientation="vertical"
          colorSpace="hsb"
          channel="alpha"
          showValueLabel={false}
        />
      </div>
      <div className="flex items-center gap-2">
        <Select selectedKey={space} onSelectionChange={(s) => setSpace(s as ColorSpace)} size="sm">
          <Item id="hex">Hex</Item>
          <Item id="rgb">RGB</Item>
          <Item id="hsl">HSL</Item>
          <Item id="hsb">HSB</Item>
        </Select>
        {space === "hex" ? (
          <ColorField aria-label="Hex" className="shrink-1 w-[40px] flex-1 basis-0" size="sm" />
        ) : (
          getColorChannels(space).map((channel) => (
            <ColorField
              key={channel}
              colorSpace={space}
              channel={channel}
              className="shrink-1 w-[40px] flex-1 basis-0"
              size="sm"
            />
          ))
        )}
      </div>
    </div>
  );
};

export type { ColorPickerProps, ColorPickerRootProps };
export { ColorPicker, ColorPickerRoot, ColorEditor };

Update the import paths to match your project setup.

Usage

Use a ColorPicker to allow users to select and edit a color in an overlay.

Options

ColorPicker accepts all button options.

<ColorPicker size="sm" shape="rectangle">
  Fill color
</ColorPicker>

Unontrolled

An initial, uncontrolled value can be provided to the ColorPicker using the defaultValue prop.

import { ColorPicker } from "@/lib/components/core/default/color-picker";

function Demo() {
  return <ColorPicker defaultValue="#5100FF" />;
}

Controlled

Use the value and onChange props to control the color picker.

const [value, setValue] = React.useState(parseColor("hsl(25, 100%, 50%)"));
return <ColorPicker value={value as Color & string} onChange={setValue} />;

Composition

If you need to customize things further, you can drop down to the composition level.

import { Button } from "@/lib/components/core/default/button";
import { ColorEditor, ColorPickerRoot } from "@/lib/components/core/default/color-picker";
import { ColorSwatch } from "@/lib/components/core/default/color-swatch";
import { Dialog, DialogRoot } from "@/lib/components/core/default/dialog";

function Demo() {
  return (
    <ColorPickerRoot defaultValue="#5100FF">
      <DialogRoot>
        <Button shape="square">
          <ColorSwatch />
        </Button>
        <Dialog type="popover" mobileType="drawer">
          <ColorEditor />
        </Dialog>
      </DialogRoot>
    </ColorPickerRoot>
  );
}

API Reference

ColorPicker extends and merges the props of the Button component.

PropTypeDefaultDescription
value
string | Color | null
-
The current value (controlled).
defaultValue
string | Color | null
-
The default value (uncontrolled).
children
ReactNode | (values: T & {defaultChildren: ReactNode | undefined}) => ReactNode
-
The children of the component. A function may be provided to alter the children based on component state.
EventTypeDescription
onChange
(value: Color) => void
Handler that is called when the value changes.

Built by mehdibha. The source code is available on GitHub.