After building dozens of React applications, I thought choosing a color picker would be simple. Just grab a popular library from npm, import it, and move on, right? Wrong. Every existing solution I tried left me frustrated, leading me to build another library and here's why.
The monolithic problem that started it all
Most React color pickers give you a single, rigid component that looks something like this:
<ColorPicker />
That's it. One big, inflexible box with everything baked in. Want to move the hue slider above the saturation area? Too bad. Need to add your own custom controls between the picker and the sliders? Not happening. Want to create a compact horizontal layout for mobile? Sorry, you're stuck with whatever the library designer thought was best.
The frustration: Every design system is different, but color pickers force you to accept their vision or hack around it with brittle CSS overrides.
The layout inflexibility problem
Here's what really bothered me: I needed a color picker for a design tool where the layout requirements kept changing. Sometimes I needed a compact horizontal layout, sometimes vertical, sometimes I wanted to inject custom controls between components.
Every library I tried gave me a pre-built layout with limited customization options. Want the alpha slider above the hue? Not possible. Need to add a custom preset palette between the saturation area and sliders? Good luck with CSS hacks.
The frustration: Why can't I just arrange the color picker components exactly how my design needs them?
The CSS override nightmare
Most color pickers come with their own CSS that you have to fight against. Want to match your design system's border radius? Override their styles. Need custom spacing? More CSS overrides. Dark mode? Hope they thought of that.
I spent more time wrestling with CSS specificity and !important declarations than actually building features. The color picker should work with your design system, not against it.
The frustration: Why am I fighting the library's CSS when I just want to pick colors?
State management that fights you
Here's a scenario that drove me crazy: I'm building a design tool where users work in HSL values, but every color picker I tried would:
- Take my HSL input
- Convert it internally to RGB or HSV
- Give me back a completely different HSL value due to rounding errors
- Force me to manage the "drift" between what the user input and what I get back
Want to initialize with { h: 240, s: 100, l: 50 } and get back exactly that format? Good luck. Most libraries have their own internal state management that assumes you want whatever format they prefer.
The frustration: I know what format I want — why won't you just give it to me?
What I built instead: react-beautiful-color
After hitting these walls repeatedly, I decided to build something different. Here's how react-beautiful-color solves each of these problems:
Compound components that actually compose
Instead of one giant component, you get composable pieces:
<ColorPicker color={colorInput} onChange={setColor}>
<ColorPicker.Saturation className="flex-1 mb-3" />
<div className="flex items-center gap-3 p-3 pt-0">
<ColorPicker.EyeDropper>
<Pipette />
</ColorPicker.EyeDropper>
<div className="flex-1 flex flex-col gap-3">
<ColorPicker.Hue className="h-4" />
<ColorPicker.Alpha className="h-4" />
</div>
</div>
</ColorPicker>
Want the hue slider on top? Move it. Need a custom layout? Build it. Want to add your own controls? Drop them anywhere. You control the layout, not the library.
True compound component architecture
Unlike other libraries that give you one monolithic component, react-beautiful-color is built from the ground up as composable pieces. Each part (Saturation, Hue, Alpha, EyeDropper) is an independent component that you can arrange however you want.
Need a custom layout? Compose it. Want to add your own components between pieces? Drop them in. Building a design system with specific spacing requirements? You control every aspect of the layout.
Format preservation that actually works
The useColorState hook preserves your input format while giving you all formats instantly:
const [{ colorInput, colorState }, setColor] = useColorState({
type: 'hsl', h: 240, s: 100, l: 50
});
// colorInput stays as HSL - no format drift!
console.log(colorInput); // { type: 'hsl', h: 240, s: 100, l: 50 }
// But you get all formats instantly
console.log(colorState.hex); // "#0000ff"
console.log(colorState.rgb); // { r: 0, g: 0, b: 255 }
console.log(colorState.hsv); // { h: 240, s: 100, v: 100 }
No conversion drift. No state fighting. Just the format you want, when you want it.
Tailwind-first styling approach
Instead of shipping custom CSS that you have to override, react-beautiful-color uses pure Tailwind classes. Your design system uses Tailwind? Perfect integration. Custom design tokens? Just pass different classes. Dark mode? It just works.
No CSS overrides, no specificity wars, no style conflicts. The components are unstyled by default and accept whatever Tailwind classes you want to apply.
Modern browser APIs
Built-in support for the EyeDropper API means users can pick colors directly from their screen — no complex implementations or polyfills required.
TypeScript from day one
Complete type safety with discriminated unions means you catch color format errors at compile time, not runtime. IntelliSense works perfectly, and refactoring is safe.
The result: happiness
Building with react-beautiful-color feels different. Instead of fighting the library, you're composing with it. Instead of working around limitations, you're building exactly what you need.
The compound component pattern means every use case I've encountered from simple color inputs to complex design tools can be built elegantly without hacks or workarounds.
You get a truly flexible compound component API that adapts to your design system instead of forcing your design system to adapt to it. Every component is independently stylable, positionable, and composable.
Sometimes the best solution isn't finding the perfect library — it's building the library you wish existed.
Documentation and examples: react-beautiful-color.dev
Top comments (0)