@cassa/hooks
Five utility hooks for common React patterns. All typed, all SSR-safe.
Import
import {
useDebounce,
useLocalStorage,
useMediaQuery,
useClickOutside,
usePrevious,
} from '@cassa/hooks'All hooks
| Prop | Type | Default | Description |
|---|---|---|---|
| useDebounce | hook | — | Delays updating a value until the user stops changing it. |
| useLocalStorage | hook | — | useState that persists to localStorage and syncs across tabs. |
| useMediaQuery | hook | — | Reactively checks a CSS media query. |
| useClickOutside | hook | — | Fires a callback when the user clicks outside a ref element. |
| usePrevious | hook | — | Returns the value from the previous render. |
useDebounce
Returns a debounced copy of the value that only updates after the delay has elapsed. Ideal for search inputs to avoid firing a request on every keystroke.
// signature
useDebounce<T>(value: T, delay: number): Timport { useDebounce } from '@cassa/hooks'
const SearchBar = () => {
const [query, setQuery] = useState('')
const debouncedQuery = useDebounce(query, 300)
useEffect(() => {
if (debouncedQuery) fetchResults(debouncedQuery)
}, [debouncedQuery]) // fires only after 300ms of no input
return <input value={query} onChange={e => setQuery(e.target.value)} />
}useLocalStorage
Drop-in replacement for useState that persists the value to localStorage and syncs across tabs. Safe on SSR — no-ops in Node.
// signature
useLocalStorage<T>(key: string, initialValue: T): [T, setter]import { useLocalStorage } from '@cassa/hooks'
const ThemeToggle = () => {
const [theme, setTheme] = useLocalStorage<'light' | 'dark'>('theme', 'light')
return (
<button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
{theme === 'light' ? '☾ Dark' : '☀ Light'}
</button>
)
}useMediaQuery
Returns true if the given CSS media query currently matches, and updates reactively when the viewport changes. Returns false on SSR.
// signature
useMediaQuery(query: string): booleanimport { useMediaQuery } from '@cassa/hooks'
const Layout = () => {
const isMobile = useMediaQuery('(max-width: 768px)')
const prefersDark = useMediaQuery('(prefers-color-scheme: dark)')
return isMobile ? <MobileLayout /> : <DesktopLayout />
}useClickOutside
Returns a ref. Calls callback whenever the user clicks outside the element the ref is attached to. Useful for closing dropdowns and modals.
// signature
useClickOutside<T extends HTMLElement>(callback: () => void): RefObject<T>import { useClickOutside } from '@cassa/hooks'
const Dropdown = ({ onClose }) => {
const ref = useClickOutside<HTMLDivElement>(onClose)
return (
<div ref={ref} className="absolute top-full bg-white shadow-lg">
Dropdown content
</div>
)
}usePrevious
Returns the previous render's value. Useful for detecting changes, animating transitions, or comparing old vs new state.
// signature
usePrevious<T>(value: T): T | undefinedimport { usePrevious } from '@cassa/hooks'
const Counter = () => {
const [count, setCount] = useState(0)
const prevCount = usePrevious(count)
return (
<div>
<p>Now: {count} | Before: {prevCount ?? '—'}</p>
<button onClick={() => setCount(c => c + 1)}>+1</button>
</div>
)
}