Getting Started
App Providerglobal.d.tsThemesTypes
cn(...args)Text ParserUnits Converters
AnchorAvatarBreadcrumbBurgerButtonCardCarouselCheckerCodeColor PickerCommandConfettiCopyButtonDouble Helix WordsFloating IndicatorGroupHighlight TextIndicatorInputKbdLabelLoaderPaginationPassword RequirementPolymorphic SlotProgressProseRatingRunning AreaScroll AreaSheetsSkeletonSliderStackSvgTableTabsTextareaTimelineTimesToasterTooltipTyping WordsTypography
useClickOutsideuseClipboarduseDeviceInfouseDialoguseDidUpdateuseDirectionuseDisclosureuseDocumentTitleuseDocumentVisibilityuseElementInfouseEyeDropperuseFetchuseFullscreenuseGeoLocationuseHotkeysuseHoveruseIduseImagePopupuseInputStateuseIntersectionuseIntervaluseIsomorphicEffectuseListStateuseLocalStorageuseMeasureScrollbaruseMediaQueryuseMergedRefuseMouseuseMoveuseMutationObserveruseNetworkuseOpenStateuseOrientationuseOSusePaginationusePWAInstalleruseRandomColorsuseReducedMotionuseReloaduseResizeObserveruseScrollIntoViewuseStateHistoryuseTimeoutuseTouchuseTriggeruseUncontrolleduseValidatedStateuseViewportSizeuseWindowEventuseWindowScroll
Docs
Web
Components
Color Picker
Code

A code display component that enhances the readability of code snippets with optional syntax highlighting.

Command

A search dialog component that provides fuzzy search for locating items quickly within the application.


Edit this page on GitHub
  • Started
  • Utilities
  • Configuration
  • Components
  • Hooks
  • Examples
  • Github
  • Contributing
⌘+J

© 2025 oeri rights MIT


Designed in Earth-616

Built by oeri

Color Picker

A tool that allows users to select colors visually, often used for customizing designs or selecting background/foreground colors.

Usage

Basic usage example to quickly see how the ColorPicker works.

"use client";
import { useState } from "react";
import { Input } from "@/ui/input";
import { ColorPicker } from "@/ui/color-picker";
import { CheckIcon } from "@/icons/*";
import { Svg } from "@/ui/svg";

const swatches = ["#ffffff", "#000000", "#2e2e2e", "#868e96", "#fa5252", "#e64980", "#be4bdb", "#639", "#4c6ef5", "#228be6", "#15aabf", "#12b886", "#40c057", "#82c91e", "#fab005", "#fd7e14"];

export function ColorPickerDemo() {
  const [value, onChange] = useState("#639");
  return (
    <div className="flex flex-wrap items-center justify-evenly gap-12">
      <Svg currentFill="fill" size="xxxl" fill={value}>
        <path d="m19.48,1H1v18.48c0,1.94,1.58,3.52,3.52,3.52h14.96c1.94,0,3.52-1.58,3.52-3.52V4.52c0-1.94-1.58-3.52-3.52-3.52Zm-10.58,18.79c.51,0,.7-.51.66-1.08h1.63c.09,1.61-.92,2.55-2.35,2.53-1.39,0-2.27-.77-2.27-2.27v-3.7c0-1.47.95-2.27,2.38-2.27,1.41-.02,2.29.9,2.24,2.46h-1.63c.04-.59-.22-1.03-.66-1.01-.55,0-.7.37-.7,1.08v3.21c0,.68.22,1.01.7,1.03Zm5.1,1.45c-1.41,0-2.2-.97-2.22-2.53h1.52c.02.7.24,1.14.73,1.14s.66-.29.66-.95c0-.55-.24-.86-.84-1.14l-.57-.26c-1.01-.48-1.43-1.08-1.43-2.27,0-1.32.84-2.24,2.2-2.24s2.09.95,2.11,2.49h-1.47c0-.64-.13-1.08-.62-1.08-.44,0-.66.22-.66.77s.2.77.73.99l.53.24c1.12.53,1.61,1.21,1.61,2.49,0,1.52-.86,2.35-2.27,2.35Zm4.97,0c-1.41,0-2.2-.97-2.22-2.53h1.54c0,.7.24,1.14.7,1.14s.66-.29.66-.95c0-.55-.22-.86-.84-1.14l-.57-.26c-1.01-.48-1.41-1.08-1.41-2.27,0-1.32.81-2.24,2.2-2.24s2.07.95,2.11,2.49h-1.47c-.02-.64-.15-1.08-.64-1.08-.44,0-.64.22-.64.77s.18.77.7.99l.55.24c1.1.53,1.58,1.21,1.58,2.49,0,1.52-.86,2.35-2.27,2.35Z" />
      </Svg>

      <ColorPicker withPicker={false} format="hsla" size={18} value={value} onChange={onChange} swatches={swatches} selectedIcon={<CheckIcon size="sm" />}>
        <Input
          value={value}
          onChange={e => onChange(e.target.value)}
          className="mt-[--cp-space] h-auto rounded-[--cp-input-round] p-[--cp-input-p] leading-none [font-size:--cp-input-fz]"
        />
      </ColorPicker>
    </div>
  );
}

Properties

Interactive configurator to explore customization options for the ColorPicker component.

Size
Round
Swatch per row
"use client";
import { useState } from "react";
import { Input } from "@/ui/input";
import { ColorPicker } from "@/ui/color-picker";

export function ColorPickerDemo() {
  const [value, onChange] = useState("rgba(47, 119, 150, 0.7)");
  const swatches = ["#ffffff", "#000000", "#2e2e2e", "#868e96", "#fa5252", "#e64980", "#be4bdb", "#639", "#4c6ef5", "#228be6", "#15aabf", "#12b886", "#40c057", "#82c91e", "#fab005", "#fd7e14" ];
  return (
      <ColorPicker format="hex" size={20} value={value} onChange={onChange} swatches={swatches}>
      <Input
        value={value}
        onChange={e => onChange(e.target.value)}
        className="mt-[--cp-space] h-auto rounded-[--cp-input-round] p-[--cp-input-p] leading-none [font-size:--cp-input-fz]"
      />
      </ColorPicker>
  );
}

Notes

withClipboard prop only available in certain formats "hexa" || "rgba" || "hsla".


API References

Styles API

type ColorPickerTrees = "saturation" | "body" | "slider" | "root" | "sliders" | "sliderOverlay" | "thumb" | "saturationOverlay" | "swatches" | "swatch" | "preview" | "alphaOverlay" | "shadowOverlay" | "colorOverlay" | "childrenOverlay";
Styles APITypeDefaultAnnotation
unstyled?Partial<Record<ColorPickerTrees, boolean>>falseif true, all default styles will be removed
classNames?Partial<Record<ColorPickerTrees, string>>undefinedSet className for ColorPickerTrees
styles?Partial<Record<ColorPickerTrees, CSSProperties>>undefinedSet style for ColorPickerTrees

Props API

type ColorFormat = "hex" | "hexa" | "rgba" | "rgb" | "hsl" | "hsla";
 
const withAlpha = format === "hexa" || format === "rgba" || format === "hsla";
Props APITypeDefaultAnnotation
defaultValue?stringundefinedUncontrolled component default value
value?stringundefinedControlled component value
format?ColorFormathexColor format
withPicker?booleantrueDetermines whether the color picker should be displayed
swatches?string[]undefinedAn array of colors in one of the supported formats. Used to render swatches list below the color picker.
swatchPerRow?number7Number of swatches per row
focusable?booleantrueDetermines whether interactive elements (sliders thumbs and swatches) should be focusable
size?string | numberControls size of hue, alpha and saturation sliders
alphaLabel?stringAlpha slider aria-label prop
hueLabel?stringHue slider aria-label prop
saturationLabel?stringSaturation slider aria-label prop
withShadow?booleantrueAdd style shadow for swatches
withClipboard?booleanfalseWhen to copy color values (only available in certain formats / withAlpha)
onChange?(value: string) => voidundefinedCalled when value changes
onChangeEnd?(value: string) => voidundefinedCalled when the user stops dragging one of the sliders or changes the value with arrow keys
onColorSwatchClick?(color: string) => voidundefinedCalled when one of the color swatches is clicked

Source Codes

Full working code example, including necessary markup and styles. You can copy and paste this code directly to start using the component immediately.

color-picker.tsx
globals.css
/* color-picker */ @layer base { .stylelayer-colorpicker { @apply [&:where([data-full-width])]:[--cp-full-width:100%] [&:where(:has([data-cp=saturation]~[data-cp=swatches],[data-cp=body]~[data-cp=swatches]))_[data-cp=swatches]]:mt-[calc(var(--cp-space)-0.125rem)] [--slider-h:calc(var(--cp-size)+2px)] [&_*]:appearance-none [&_*]:[-webkit-tap-highlight-color:transparent] [--rectangles:0_0/0.5rem_0.5rem_linear-gradient(45deg,hsl(var(--muted))_25%,transparent_25%),0_0.25rem_/0.5rem_0.5rem_linear-gradient(-45deg,hsl(var(--muted))_25%,transparent_25%),0.25rem_-0.25rem/0.5rem_0.5rem_linear-gradient(45deg,transparent_75%,hsl(var(--muted))_75%),-0.25rem_0/0.5rem_0.5rem_linear-gradient(-45deg,hsl(var(--background))_75%,hsl(var(--muted))_75%)] [--overlay:hsl(var(--background))_0_0_0_calc(0.075rem*1)_inset,hsl(var(--muted))_0_0_calc(0.25rem*1)inset]; & [data-cp="saturation"] { @apply [&:where([data-focus-ring=auto]):focus:focus-visible_[data-cp=thumb]]:[outline:2px_solid_hsl(var(--constructive))] [&:where([data-focus-ring=always]):focus_[data-cp=thumb]]:[outline:2px_solid_hsl(var(--constructive))]; } & [data-cp="slider"] { @apply [&:not(:first-child)]:mt-[--cp-space-slider] [&:where([data-focus-ring=auto]):focus:focus-visible_[data-cp=thumb]]:[outline:2px_solid_hsl(var(--constructive))] [&:where([data-focus-ring=always]):focus_[data-cp=thumb]]:[outline:2px_solid_hsl(var(--constructive))]; } & [data-cp="preview"] { @apply [--h:--cp-preview-sz] [--w:--cp-preview-sz] [--round:--cp-round]; &[data-clipboard] { @apply cursor-pointer transition-transform active:scale-[.96]; } } & [data-cp="thumb"] { @apply [--left:calc(var(--thumb-x-offset)-var(--cp-size)/2)] [--top:calc(var(--thumb-y-offset)-var(--cp-size)/2)]; } & [data-cp="swatch"] { @apply transition-transform active:scale-[.96] min-h-[--h] min-w-[--w] [--h:var(--cp-swatch-h,max(var(--cp-swatch-size,unset),calc(var(--cp-preview-sz)/2)))] [--w:--cp-swatch-w,var(--cp-swatch-size,unset)] [--rows:calc(var(--cp-swatch-row)-0.25rem)]; } & [data-cp="swatches"] { @apply -mx-0.5 flex flex-wrap; } } }