Browser color picker hook for React.
Implements the EyeDropper API into an easy-to-use React hook. This API is currently only available in Chromium based browsers.
isSupported
method.close
method to cancel eye dropper (signals can still be used).import React, { useState, useCallback } from 'react'
import useEyeDropper from 'use-eye-dropper'
const App = () => {
const { open, close, isSupported } = useEyeDropper()
const [color, setColor] = useState('#fff')
const [error, setError] = useState()
// useEyeDropper will reject/cleanup the open() promise on unmount,
// so setState never fires when the component is unmounted.
const pickColor = useCallback(() => {
// Using async/await (can be used as a promise as-well)
const openPicker = async () => {
try {
const color = await open()
setColor(color.sRGBHex)
} catch (e) {
console.log(e)
// Ensures component is still mounted
// before calling setState
if (!e.canceled) setError(e)
}
}
openPicker()
}, [open])
return (
<>
<div style={{ padding: '64px', background: color }}>Selected color</div>
{isSupported() ?
<button onClick={pickColor}>Pick color</button>
: <span>EyeDropper API not supported in this browser</span>
}
{!!error && <div>{error.message}</div>}
</>
)
}
With TypeScript all of the types will be inferred where possible, however if you need to use error
handling some type-guard functions can be used to narrow down the type of the
error from the catch block. Otherwise it will be unknown
.
Here is one approach to deal with this when working with TypeScript and dropper errors:
type DropperError = {
message: string
canceled?: boolean
}
const isError = <T, >(err: DropperError | T): err is DropperError =>
!!err && err instanceof Error && !!err.message
const isNotCanceled = <T, >(err: DropperError | T): err is DropperError =>
isError(err) && !err.canceled
and then !e.canceled
can be replaced with isNotCanceled(e)
, the type-guard will enforce a proper type when using setError
.
This will also work for isError
.
open({ signal?: AbortSignal }) => Promise<{ sRGBHex: string }>'
rgba
value.close() => void
open
. Otherwise this
performs a no-op.isSupported() => boolean