Color Converter

Convert colors between HEX, RGB, and HSL formats with a live color preview swatch.

Design
100% Free
Runs in Browser

Preview

Source Code

Toggle the controls below the code to live-edit the initial state values.

color-converter.tsx
import { useState } from "react";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Copy, Check } from "lucide-react";

function hexToRgb(hex: string): [number, number, number] | null {
    const m = hex.replace("#", "").match(/^([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);
    return m ? [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)] : null;
}

function rgbToHsl(r: number, g: number, b: number): [number, number, number] {
    r /= 255; g /= 255; b /= 255;
    const max = Math.max(r, g, b), min = Math.min(r, g, b);
    let h = 0, s: number;
    const l = (max + min) / 2;
    if (max === min) {
        h = s = 0;
    } else {
        const d = max - min;
        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
        switch (max) {
            case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
            case g: h = ((b - r) / d + 2) / 6; break;
            case b: h = ((r - g) / d + 4) / 6; break;
        }
    }
    return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];
}

export default function ColorConverter() {
    const [hex, setHex] = useState("#6366f1");
    const [copied, setCopied] = useState("");

    const rgb = hexToRgb(hex);
    const hsl = rgb ? rgbToHsl(...rgb) : null;

    const formats = [
        { label: "HEX", value: hex.toUpperCase() },
        { label: "RGB", value: rgb ? `rgb(${rgb.join(", ")})` : "—" },
        { label: "HSL", value: hsl ? `hsl(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%)` : "—" },
        { label: "CSS var", value: rgb ? `${rgb.join(" ")}` : "—" },
    ];

    const copy = async (label: string, value: string) => {
        await navigator.clipboard.writeText(value);
        setCopied(label);
        setTimeout(() => setCopied(""), 1500);
    };

    return (
        <div className="space-y-4">
            <div className="flex items-center gap-3">
                <div
                    className="w-14 h-14 rounded-lg border shadow-sm shrink-0"
                    style={{ backgroundColor: hex }}
                />
                <Input
                    type="text"
                    value={hex}
                    onChange={(e) => setHex(e.target.value)}
                    placeholder="#6366f1"
                    className="font-mono max-w-[200px]"
                />
                <input
                    type="color"
                    value={hex}
                    onChange={(e) => setHex(e.target.value)}
                    className="w-10 h-10 rounded border cursor-pointer"
                />
            </div>
            <div className="grid gap-2 sm:grid-cols-2">
                {formats.map((f) => (
                    <div
                        key={f.label}
                        className="flex items-center justify-between rounded-lg border px-3 py-2"
                    >
                        <div className="flex items-center gap-2">
                            <Badge variant="outline" className="text-xs">
                                {f.label}
                            </Badge>
                            <code className="text-sm font-mono">{f.value}</code>
                        </div>
                        <Button
                            variant="ghost"
                            size="icon"
                            className="h-7 w-7"
                            onClick={() => copy(f.label, f.value)}
                        >
                            {copied === f.label ? (
                                <Check className="h-3.5 w-3.5" />
                            ) : (
                                <Copy className="h-3.5 w-3.5" />
                            )}
                        </Button>
                    </div>
                ))}
            </div>
        </div>
    );
}
Props Playground
1 prop

Required Libraries

Install the external dependencies used by this tool.

bun add lucide-react react

Shadcn UI Setup

Add the required Shadcn UI primitives to your project.

bun x --bun shadcn@latest add badge button input

Shadcn UI Components

The following primitives are required in your @/components/ui directory.

Component Import Path
badge @/components/ui/badge
button @/components/ui/button
input @/components/ui/input

Imports

All import statements used in this tool.

Source Exports
react useState
@/components/ui/input Input
@/components/ui/button Button
@/components/ui/badge Badge
lucide-react Copy, Check

State Management

React state variables managed within this tool.

Variable Initial Value
hex "#6366f1"
copied ""

Variables & Constants

Constants and computed values defined in this tool.

Name Value
m hex.replace("#", "").match(/^([a-f\d]{2})([a-f\d]{2})([a-...
max Math.max(r, g, b), min = Math.min(r, g, b)
d max - min
rgb hexToRgb(hex)
hsl rgb ? rgbToHsl(...rgb) : null
formats [

Functional Logic

Internal functions that handle the tool's core logic.

Function Parameters Async
hexToRgb() hex: string No
rgbToHsl() r: number, g: number, b: number No
copy() label: string, value: string
Yes

External Resources

Documentation, tutorials, and package details for libraries used in this tool.