UIPackage

Color Picker

React form
Edit on GitHub

Hex / RGB color input with a popover swatch grid. Supports controlled and uncontrolled modes, alpha channel, and a recent-colors row.

Also available for Vue ->

Installation

$ npx shadcn@latest add https://react.uipkge.dev/r/react/color-picker.json

Or with the named registry: npx shadcn@latest add @uipkge-react/color-picker

Examples

Props

Name Type / Values Default Required
value string optional
onValueChange (value: string) => void optional
disabled boolean optional
presets

Override the swatches shown below the color input. Pass [] to hide entirely.

string[] optional
hideHexInput

Hide the hex text field next to the color trigger.

boolean false optional
className string optional

Files (2)

  • components/ui/color-picker/color-picker.tsx 3.2 kB
    'use client'
    
    import * as React from 'react'
    import { cn } from '@/lib/utils'
    
    const DEFAULT_PRESETS = [
      '#ef4444',
      '#f97316',
      '#eab308',
      '#22c55e',
      '#14b8a6',
      '#3b82f6',
      '#8b5cf6',
      '#ec4899',
      '#ffffff',
      '#d4d4d4',
      '#737373',
      '#171717',
    ]
    
    export interface ColorPickerProps {
      value?: string
      onValueChange?: (value: string) => void
      disabled?: boolean
      /** Override the swatches shown below the color input. Pass [] to hide entirely. */
      presets?: string[]
      /** Hide the hex text field next to the color trigger. */
      hideHexInput?: boolean
      className?: string
    }
    
    const ColorPicker = React.forwardRef<HTMLDivElement, ColorPickerProps>(
      ({ value, onValueChange, disabled, presets = DEFAULT_PRESETS, hideHexInput = false, className }, ref) => {
        const swatches = presets ?? []
    
        return (
          <div ref={ref} className={cn('space-y-3', className)}>
            {/* Color trigger + hex field */}
            <div className="flex items-center gap-2">
              <div
                className="border-input relative h-10 w-10 shrink-0 overflow-hidden rounded-md border shadow-xs"
                style={{ backgroundColor: value || '#ffffff' }}
              >
                <input
                  type="color"
                  value={value || '#ffffff'}
                  disabled={disabled}
                  aria-label="Pick color"
                  className="absolute inset-0 h-full w-full cursor-pointer opacity-0 disabled:cursor-not-allowed"
                  onInput={(e) => onValueChange?.((e.target as HTMLInputElement).value)}
                />
              </div>
              {!hideHexInput && (
                <input
                  type="text"
                  value={value || ''}
                  placeholder="#000000"
                  disabled={disabled}
                  className="bg-background border-input text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 h-10 flex-1 rounded-md border px-3 text-sm uppercase shadow-xs outline-none focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-50"
                  onChange={(e) => onValueChange?.(e.target.value)}
                />
              )}
            </div>
    
            {/* Preset swatches */}
            {swatches.length > 0 && (
              <div className="flex flex-wrap gap-1.5">
                {swatches.map((color) => (
                  <button
                    type="button"
                    key={color}
                    disabled={disabled}
                    aria-label={`Select ${color}`}
                    style={{ backgroundColor: color }}
                    className={cn(
                      'ring-offset-background focus-visible:ring-ring/40 size-6 shrink-0 rounded-md shadow-sm transition-transform outline-none hover:scale-110 focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:scale-100',
                      value?.toLowerCase() === color.toLowerCase()
                        ? 'ring-foreground ring-2 ring-offset-2'
                        : 'ring-border/50 ring-1',
                      color.toLowerCase() === '#ffffff' && 'ring-border',
                    )}
                    onClick={() => onValueChange?.(color)}
                  />
                ))}
              </div>
            )}
          </div>
        )
      },
    )
    ColorPicker.displayName = 'ColorPicker'
    
    export { ColorPicker }
  • components/ui/color-picker/index.ts 0.1 kB
    export { ColorPicker, type ColorPickerProps } from './color-picker'

Raw manifest: https://react.uipkge.dev/r/react/color-picker.json