usehooks
A collection of modern, server-safe React hooks for browser APIs, storage, and UI interaction patterns from the ui.dev team.
Repository Health
Technical Analysis
usehooks (published as @uidotdev/usehooks) is a single-file, dependency-free collection of roughly three dozen React hooks covering the browser-API and UI-state problems most apps eventually reimplement themselves: battery status, geolocation, network state, clipboard access, local/session storage, intersection and resize observers, media queries, mouse/hover tracking, debouncing/throttling, and simple undo/redo history state.
Each hook is exported individually from one ESM entry point with no build step and no runtime dependencies beyond React itself (peer dependency on React 18+). Several of the browser-state hooks (useMediaQuery, useLocalStorage, useSessionStorage, useNetworkState, usePreferredLanguage, useVisibilityChange) are built on React’s useSyncExternalStore, making them safe under concurrent rendering rather than relying on ad hoc effect-driven state that can tear during concurrent updates. The project also publishes an experimental channel (@uidotdev/usehooks@experimental) with additional hooks like useFetch, useKeyPress, and useInterval that haven’t been promoted to the stable release.
What You Get
- ~37 standalone hooks covering storage (useLocalStorage, useSessionStorage), device/browser state (useBattery, useGeolocation, useNetworkState, useOrientation, usePreferredLanguage), and observers (useIntersectionObserver, useMeasure)
- Concurrent-safe implementations of the storage and media-query hooks built on React’s useSyncExternalStore, avoiding the tearing bugs older effect-based equivalents can hit
- Interaction hooks for common UI patterns: useClickAway, useHover, useLongPress, useMouse, useDebounce, useThrottle, useToggle
- State-container hooks that go beyond useState: useHistoryState (undo/redo), useMap, useSet, useQueue, useList, useObjectState
- Hand-written TypeScript declarations (index.d.ts) shipped alongside the ESM source, with no build/bundling step required to consume the package
- An experimental release channel (@uidotdev/usehooks@experimental) previewing additional hooks such as useFetch, useInterval, useKeyPress, and useEventListener before they’re promoted to stable
Common Use Cases
- Persisting UI state (theme, form drafts, filters) to localStorage/sessionStorage with automatic cross-tab sync via useLocalStorage/useSessionStorage
- Reacting to connectivity and device conditions (battery level, network type, online/offline) to adapt UI behavior on constrained devices
- Building interaction affordances like click-away dismissal, long-press, hover tracking, and lazy-load triggers via useIntersectionObserver
- Debouncing or throttling rapidly changing values (search input, scroll position, resize events) without hand-rolling timers in every component
- Adding undo/redo to editors or wizards with useHistoryState instead of building a custom reducer for history tracking
Under The Hood
Architecture
The entire package is a single flat ESM file (index.js, ~1,370 lines) exporting roughly three dozen independent hook functions plus a handful of private, module-scoped helpers (isShallowEqual, throttle, dispatchStorageEvent, isPlainObject, isTouchEvent/isMouseEvent) shared by a few related hooks (useIdle reuses throttle, useLongPress reuses the event-type checks, the storage hooks share dispatchStorageEvent). There’s no shared runtime object graph or internal module boundary to reason about — each hook is self-contained and composed directly from React’s own primitives (useState, useEffect, useLayoutEffect, useSyncExternalStore, useReducer, useRef, useMemo, useCallback), so removing or changing one hook cannot break another; the only coupling risk is within the small set of shared private helpers.
Tech Stack
The package has no runtime dependencies and a single peer dependency on react/react-dom >=18.0.0 (pinned to ^18.2.0 in devDependencies alongside @types/react ^18.2.20 and typescript ^5.1.6). It ships zero build output — package.json’s “exports” field points directly at the hand-authored index.js (ESM, “type”: “module”), and index.d.ts is a hand-written declaration file checked locally against a strict tsconfig.json (ESNext target/module, strict mode, allowJs) rather than emitted by a compiler step. The repository also contains a separate usehooks.com/ subdirectory — an Astro-based documentation site with a live demo per hook — that is entirely decoupled from the published npm package.
Code Quality
No test files exist anywhere in the package source, and the repository has no CI configuration (no .github/workflows) to run type-checks, lint, or tests on push. Naming is consistent (useXxx hooks, camelCase helpers) and effect cleanup is handled correctly throughout (every addEventListener/observer pairs with a matching removal in the effect’s return function), but there is no automated way to verify the hand-written index.d.ts stays in sync with index.js, and no linter or formatter configuration is present at the package root. Error handling is inconsistent across hooks: useCounter and useWindowScroll throw explicit errors on invalid input, while useLocalStorage/useSessionStorage swallow JSON-parse failures behind a bare console.warn.
API Design The library’s core ergonomic choice is exposing several browser-state hooks (useLocalStorage, useSessionStorage, useMediaQuery, useNetworkState, usePreferredLanguage, useVisibilityChange) through useSyncExternalStore rather than the effect-plus-state pattern common in older hook collections, making them correctly tearing-safe under React 18’s concurrent renderer — a real correctness advantage over collections written before that API existed. Every hook is zero-config and independently importable with no provider or setup step, keeping the surface area small and predictable, though several hooks (useBattery, useGeolocation) are thin wrappers around their underlying Web APIs with limited abstraction beyond removing effect boilerplate, and SSR safety is handled ad hoc — some hooks simply throw (“useMediaQuery is a client-only hook”) when their server snapshot is invoked, leaving guard logic to the consumer.
Used by 15 apps in this directory
OpenHands
AI Code Assistants · AI Development
The self-hosted developer control center for running AI coding agents — locally, in Docker, on VMs, or across cloud backends — with automation workflows for GitHub, Slack, and more.
Postiz
Automation · Social Media
The agentic social media scheduler — AI-powered content creation, 33-platform posting, and team workflows, all self-hosted.
Rybbit
Analytics
Open-source, privacy-friendly web and product analytics with cookieless tracking, session replays, funnels, and real-time dashboards — a self-hostable alternative to Google Analytics.
Sourcebot
AI Code Assistants · Developer Tools · Search
A self-hosted, AI-powered code search engine that indexes every repo across GitHub, GitLab, Bitbucket, Gitea, Gerrit, and Azure DevOps, so both engineers and coding agents can search, browse, and ask questions about your codebase from one place.
Supabase
Authentication · Databases · Developer Tools
The open-source Postgres development platform that replaces Firebase with authentication, real-time APIs, edge functions, storage, and vector embeddings — all built on PostgreSQL.
Tabby
AI Code Assistants
Self-hosted AI coding assistant — run GitHub Copilot-grade code completion on your own hardware with no cloud dependency.