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.
color-picker.tsx
button.tsx
color-area.tsx
color-field.tsx
color-swatch.tsx
dialog.tsx
overlay.tsx
select.tsx
list-box.tsx
"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.
Prop | Type | Default | Description |
---|
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. |
Event | Type | Description |
---|
onChange | (value: Color) => void | Handler that is called when the value changes. |