useEyeDropper
A specialized utility hook for selecting colors from anywhere on the screen using the modern EyeDropper API, providing hex color codes effortlessly.
Installation
npx shadcn@latest add https://r.fiberui.com/r/hooks/use-eye-dropper.jsonA React hook that provides access to the EyeDropper API, allowing users to sample colors from anywhere on their screen—even outside the browser window.
Browser Support
This API is currently supported primarily in Chromium-based browsers
(Chrome, Edge, Opera) on desktop. It is not supported on Firefox or
Safari yet. Use isSupported to provide a fallback.
Features
- Pick Anywhere - Select colors from other apps, desktop wallpaper, etc.
- Native UI - Uses the browser's native magnifying glass UI
- Support Check -
isSupportedflag for progressive enhancement - Type Safe - Fully typed for TypeScript
Basic Usage
A color picker button that samples any pixel on the screen.
"use client";
import { useEyeDropper } from "@repo/hooks/utility/use-eye-dropper";
import { Button } from "@repo/ui/components/button";
import { Pipette } from "lucide-react";
import { toast } from "sonner";
export function Example1() {
const { open, isSupported, color, isLoading } = useEyeDropper();
const handlePickColor = async () => {
try {
const result = await open();
if (result) {
toast.success(`Color picked: ${result.sRGBHex}`);
}
} catch {
// Error is already handled in hook, but we can access it here too
toast.error("Failed to pick color");
}
};
if (!isSupported) {
return (
<div className="rounded-md bg-yellow-100 p-4 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-200">
EyeDropper API is not supported in this browser (Chrome/Edge
only).
</div>
);
}
return (
<div className="flex flex-col items-center gap-4">
<div
className="h-32 w-32 rounded-xl border-4 border-white shadow-lg transition-colors duration-200 dark:border-zinc-800"
style={{ backgroundColor: color || "#000000" }}
/>
<div className="flex items-center gap-2">
<Button
onClick={handlePickColor}
isDisabled={isLoading}
className="gap-2"
>
<Pipette className="h-4 w-4" />
{isLoading ? "Picking..." : "Pick Color"}
</Button>
{color && (
<div className="rounded bg-zinc-100 px-2 py-1 font-mono text-sm dark:bg-zinc-800">
{color}
</div>
)}
</div>
<p className="max-w-xs text-center text-sm text-zinc-500">
Click the button and select any pixel on your screen (even
outside the browser!)
</p>
</div>
);
}
API Reference
Hook Signature
function useEyeDropper(): UseEyeDropperReturn;Return Value
| Property | Type | Description |
|---|---|---|
open | (options?) => Promise<EyeDropperResult | null> | Opens the eye dropper mode. Returns result object |
isSupported | boolean | true if EyeDropper API is available |
isLoading | boolean | true while selecting a color |
color | string | null | The hex sRGB color string (e.g. #ff0000) |
error | Error | null | Error object if selection failed or cancelled |
EyeDropperResult
interface EyeDropperResult {
sRGBHex: string; // The selected color in hex format
}Hook Source Code
import { useState, useCallback } from "react";
/**
* Options for opening the EyeDropper
*/
export interface UseEyeDropperOptions {
/** Signal to abort the selection */
signal?: AbortSignal;
}
/**
* Result from the EyeDropper API
*/
export interface EyeDropperResult {
/** The selected color in hex sRGB format (e.g. #ff0000) */
sRGBHex: string;
}
/**
* Return type for the useEyeDropper hook
*/
export interface UseEyeDropperReturn {
/** Open the eye dropper to select a color */
open: (options?: UseEyeDropperOptions) => Promise<EyeDropperResult | null>;
/** Whether the EyeDropper API is supported */
isSupported: boolean;
/** Whether the eye dropper is currently open */
isLoading: boolean;
/** The last selected color */
color: string | null;
/** Error from the last operation */
error: Error | null;
}
// Add type definition for EyeDropper if not present in environment
interface EyeDropper {
open(options?: { signal?: AbortSignal }): Promise<{ sRGBHex: string }>;
}
interface EyeDropperConstructor {
new (): EyeDropper;
prototype: EyeDropper;
}
declare global {
interface Window {
EyeDropper?: EyeDropperConstructor;
}
}
/**
* A React hook for using the EyeDropper API to select colors from the screen.
*
* @returns UseEyeDropperReturn object with open function and state
*
* @example
* ```tsx
* const { open, color, isSupported } = useEyeDropper();
*
* return (
* <div>
* <button onClick={open} disabled={!isSupported}>Pick Color</button>
* {color && <div style={{ backgroundColor: color }}>Selected: {color}</div>}
* </div>
* );
* ```
*/
export function useEyeDropper(): UseEyeDropperReturn {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const [color, setColor] = useState<string | null>(null);
// Check support
const isSupported = typeof window !== "undefined" && "EyeDropper" in window;
// Open eye dropper
const open = useCallback(
async (
options?: UseEyeDropperOptions,
): Promise<EyeDropperResult | null> => {
if (!isSupported) {
setError(new Error("EyeDropper API is not supported"));
return null;
}
setIsLoading(true);
setError(null);
try {
// @ts-ignore - EyeDropper is experimental
const eyeDropper = new window.EyeDropper();
const result = await eyeDropper.open(options);
setColor(result.sRGBHex);
return result;
} catch (err) {
// User cancelation is common error
const error =
err instanceof Error
? err
: new Error("Failed to pick color");
setError(error);
return null;
} finally {
setIsLoading(false);
}
},
[isSupported],
);
return {
open,
isSupported,
isLoading,
color,
error,
};
}
export default useEyeDropper;
useDebouncedState
A hook that delays updating a state value until a specified time has passed. Essential for reducing API calls and expensive re-renders during rapid user input.
useInterval
A declarative React hook for setInterval that is stale-closure safe, pausable via null delay, and automatically cleaned up on unmount. Perfect for polling, counters, and animations.