React hooks for the parts you keep rewriting: storage, media queries, clicks outside, debouncing, observers, timers.
npm install reactilitiesimport { useState } from 'react';
import { useLocalStorage, useDebounce, useClickOutside } from 'reactilities';
function Search() {
const [recent, setRecent] = useLocalStorage<string[]>('recent', []);
const [query, setQuery] = useState('');
const [isOpen, setIsOpen] = useState(false);
const debouncedQuery = useDebounce(query, 300);
const panelRef = useClickOutside<HTMLDivElement>(() => setIsOpen(false));
return (
<div ref={panelRef}>
<input
value={query}
onFocus={() => setIsOpen(true)}
onChange={(e) => setQuery(e.target.value)}
/>
{isOpen && (
<Results
query={debouncedQuery}
recent={recent}
onPick={(pick) => setRecent([pick, ...recent])}
/>
)}
</div>
);
}Every hook is also its own entry point, so you can pull in one without the rest:
reactilities/useDebounce is 420 bytes.
React 18 or newer, tested on 18 and 19. Several hooks are built on
useSyncExternalStore, which 18 introduced.
react-dom is an optional peer dependency. No hook imports it; you need it only
if you render portals yourself.
Import from the package root or from a hook's own subpath. Both tree-shake:
import { useDebounce, useLocalStorage } from 'reactilities';
import { useDebounce } from 'reactilities/useDebounce';Every hook ships with the "use client" directive, so Client Components in the
App Router can import them without a wrapper.
Hooks that read browser-only state (useMediaQuery, useLocalStorage,
useNetworkState, useWindowSize, and others) return a stable server snapshot
during SSR rather than throwing. The server render and the first client render
agree, so there is no hydration mismatch; the real value applies after mount.
Two conventions run through the library, so learning one hook teaches you most of the rest.
Hooks give you the ref; you attach it. No hook asks you to create a ref and pass it in:
const ref = useClickOutside<HTMLDivElement>(close);
const [hoverRef, isHovered] = useHover<HTMLDivElement>();
const { ref: sizeRef, width } = useResizeObserver<HTMLDivElement>();
<div ref={ref} />These are callback refs, so React tells the hook when the element mounts, is
replaced, or goes away. Conditionally rendered elements need no extra wiring.
Each ref also exposes .current if you want to read the node. Use
useMergedRefs to put two of them on the same element.
The exception is useEventListener, which takes its target (window,
document, or an element) directly, since that target is usually not something
your component renders.
Return shapes follow useState. A hook built around a single value returns a
tuple, so you name the parts at the call site:
const [theme, setTheme] = useLocalStorage('theme', 'light');
const [consent, setConsent, clearConsent] = useCookie('consent');A hook with several independent results returns an object with named fields:
const { ref, width, height } = useResizeObserver<HTMLDivElement>();
const { data, error, loading, execute } = useAsync(fetchUsers);useHover is the one place a tuple holds two unrelated things, [ref, isHovered],
because naming them at the call site reads better than hover.ref.
Type inference works without annotations, and generics are there when you want to be explicit:
const [user, setUser] = useLocalStorage('user', { name: 'Ada', age: 36 });
// user: { name: string; age: number }
const { data } = useAsync<User[]>(fetchUsers);
// data: User[] | nullA few shared types are exported as well: HookRef, Nullable, Maybe,
ValueOf, and DeepPartial.
Type resolution is checked with publint and are-the-types-wrong under
bundler, node16, and nodenext.
Every hook has its own page with examples and notes.
Hooks bound to a specific element or to the document.
- useClickOutside - Detect clicks outside an element
- useDarkMode - Dark mode with system preference detection and persistence
- useDocumentTitle - Dynamically update the document title
- useDragAndDrop - File drag-and-drop with type, count, and size validation
- useEventListener - Add event listeners with automatic cleanup
- useFavicon - Dynamically update the website favicon
- useFocusTrap - Trap keyboard focus within a container (modals, drawers)
- useFullscreen - Enter, exit, and track fullscreen state on any element
- useHover - Detect hover state on any element
- useImageLazyLoad - Lazy-load images when they enter the viewport
- useInfiniteScroll - Infinite scroll via IntersectionObserver
- useIntersectionObserver - Detect element visibility (lazy loading, infinite scroll)
- useKeyboardShortcuts - Handle keyboard shortcuts
- useLockBodyScroll - Prevent body scrolling (for modals/overlays)
- useLongPress - Detect long press / hold on any element
- useMergedRefs - Combine several refs into the one an element can accept
- useMutationObserver - Watch an element for DOM changes made outside React
- usePortal - Create a DOM portal container appended to document.body
- useResizeObserver - Observe element size changes with ResizeObserver
- useRovingTabIndex - Keyboard-navigable lists and menus with roving tabIndex (ARIA)
- useScrollTo - Programmatic scroll control (top, bottom, element, or position)
- useVirtualization - Virtualize large lists for performance
Hooks that hold and manage state.
- useBoolean - Boolean state with named setTrue/setFalse/toggle controls
- useClipboard - Read from and write to the clipboard with hasCopied tracking
- useControllableState - State that works controlled or uncontrolled (for reusable components)
- useCookie - Read and write browser cookies
- useCounter - Numeric counter with min/max bounds and step
- useList - Manage array state with push, remove, sort, and more
- useLocalStorage - Persist state in localStorage with sync
- useMap - Manage Map state with React integration
- useObjectState - Manage object state with partial updates
- usePrevious - Access the previous render's value
- useRafState - State updates coalesced to one per animation frame
- useSessionStorage - Persist state in sessionStorage
- useSet - Manage Set state with React integration
- useStep - Multi-step wizard and onboarding flow state management
- useUndoRedo - State with full undo/redo history
Read-only hooks for device, browser, and viewport state.
- useBattery - Monitor device battery status via the Battery Status API
- useGeolocation - Access user's geolocation
- useMediaQuery - Responsive design with CSS media queries
- useMousePosition - Track real-time cursor position
- useNetworkState - Monitor network connectivity
- useOrientation - Track device screen orientation (portrait/landscape)
- usePageVisibility - Detect when browser tab is hidden or visible
- usePermission - Query browser permission status reactively
- useScrollPosition - Track window scroll position
- useSpeechRecognition - Voice input via the Web Speech Recognition API
- useSpeechSynthesis - Text-to-speech via the Web Speech Synthesis API
- useWindowSize - Track browser window dimensions
Debouncing, throttling, intervals, timeouts, and countdowns.
- useCountdown - Countdown and count-up timer with controls
- useDebounce - Debounce rapidly changing values
- useDebounceCallback - Debounce a callback function (vs useDebounce which debounces values)
- useIdleTimeout - Detect user inactivity after a configurable timeout
- useInterval - Run a callback on a fixed interval
- useThrottle - Throttle rapidly changing values
- useThrottleCallback - Throttle a callback function (vs useThrottle which throttles values)
- useTimeout - Run a callback after a delay with reset/clear
Async work and long-lived connections.
- useAsync - Manage async operations with loading/error/success states
- useAutoSave - Auto-save data after a debounce delay with pending/saved/error status
- useEventSource - Manage Server-Sent Events (SSE) connections
- useFileReader - Read File objects as text, data URL, or ArrayBuffer
- useWebSocket - Manage WebSocket connections
- useWorker - Run heavy computations in a Web Worker off the main thread
Effect timing and render-lifecycle helpers.
- useDidMount - Run function after component mounts
- useDidUpdate - Run function after every render
- useWillMount - Run function before first render
- useWillUnmount - Run cleanup before unmount
- useDeepCompareEffect - useEffect with structural (deep) dependency comparison
- useIsFirstRender - Whether this is the component's first render
- useIsMounted - Guard async state updates against unmounted components
- useIsomorphicLayoutEffect - SSR-safe drop-in for useLayoutEffect
- useManualUpdate - Manually trigger a component re-render
- useUpdateEffect - useEffect that skips the first render
Debugging aids. Intended for development only.
- useLogger - Log component mount, update, and unmount events (dev only)
- useWhyDidYouRender - Debug unnecessary re-renders by logging what changed (dev only)
Utility functions for common tasks.
- classnames - Conditionally join CSS class names (aliased as
cn) - loadScript - Dynamically load external scripts
Bug reports and pull requests are welcome. See CONTRIBUTING.md for how to run the test suite and the documentation fixtures.
See CHANGELOG.md, kept in the Keep a Changelog format.
Reactilities is pre-1.0 and is being tightened toward a stable API. Breaking changes are batched into as few releases as possible, and every removal ships with a replacement in MIGRATION.md.
If you find this library useful and are feeling generous, consider donating to Svratište — a day center in Belgrade providing support, meals, and shelter for people experiencing homelessness.
MIT © Petar Basic