Jesse A. Green / notebook

react-use Admin & Architecture Guide

Generated June 2026 from streamich/react-use at 9ef9535

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

This guide is for engineers who need to understand react-use below the surface: library architects evaluating it as a dependency, contributors extending it, and teams auditing it for production use. It documents the repository structure, the recurring design patterns that make ~100 hooks consistent, the build and distribution model, security considerations, and the project's maintenance status.

For day-to-day hook usage, see the User Guide. This guide assumes you have read it.


1. Executive Summary

react-use is a TypeScript library that exports a flat collection of roughly 100 React Hooks from a single entry point (src/index.ts). It is published to npm as react-use (current source version 17.6.0) under the Unlicense (public domain).

Attribute Value
Type React Hooks library (npm package)
Language TypeScript (compiled to CJS + ESM)
Entry point src/index.ts (barrel of named re-exports)
Public surface ~100 hooks + 6 factory functions
Runtime deps 14 small single-purpose libraries (see §6)
Peer deps react, react-dom (version range *)
Build tsc → lib/ (CJS) and esm/ (ESM)
Tests Jest + Testing Library (browser + SSR configs)
Docs/demo Storybook (stories/)
License Unlicense (public domain)

Important

An open maintenance issue (#2703, "Archiving react-use because it's unmaintained") signals the project is effectively in maintenance mode. The package remains installable and widely used, but new feature development has slowed. Factor this into long-term dependency decisions — see §9 Maintenance & Roadmap.


2. Architecture

2.1 The big picture

react-use has no runtime of its own — it is a set of independent hooks that run inside the host application's React render loop. There is no central store, no provider requirement (except where a hook explicitly creates one), and no shared mutable global state beyond a handful of module-level caches.

                     ┌─────────────────────────────────────┐
   import { useX }   │           src/index.ts              │
   from 'react-use'  │      (barrel: named re-exports)      │
        │            └───────────────┬─────────────────────┘
        ▼                            │
  ┌───────────────┐   each hook is a standalone module
  │  Host React   │            ┌─────┴──────┬───────────┬──────────┐
  │  component    │            ▼            ▼           ▼          ▼
  │  calls useX() │      src/useX.ts   src/useY.ts  src/factory/  src/misc/
  └───────────────┘      (uses React    (composes    create*()    util, types,
                          primitives)    other hooks) factories    hookState)
                               │            │            │          │
                               └────────────┴────────────┴──────────┘
                                            │
                                   shared helpers: on/off,
                                   isBrowser, isNavigator,
                                   resolveHookState, isDeepEqual

Three structural facts define the architecture:

  1. One hook per file. Every public hook lives in its own src/use*.ts(x) module and is re-exported by name from src/index.ts. This is what makes tree-shaking effective ("sideEffects": false).
  2. Hooks compose hooks. Higher-level hooks are built from lower-level ones — e.g. useAsync wraps useAsyncFn; useCounter wraps useGetSet; useBoolean is literally useToggle; useNumber is useCounter. The dependency graph is internal and acyclic.
  3. A thin shared core. A small src/misc/ layer (event helpers, environment flags, state resolution, deep-equality) is imported across dozens of hooks and is the closest thing to a "kernel."

2.2 Module layout

Path Role
src/index.ts Public barrel. The single source of truth for what is exported.
src/use*.ts(x) One file per hook (~100 files). The bulk of the library.
src/factory/ Factory functions that produce hooks/providers (create*).
src/misc/ Shared internal helpers (not part of the public API).
src/component/ A render-prop component wrapper (legacy ergonomics).
stories/ Storybook stories — the live demo site and de-facto usage docs.
tests/ Jest unit tests, mirroring the src/ layout.
docs/ Markdown documentation per hook.

2.3 The misc/ core

src/misc/util.ts is imported by a large share of the hooks. It provides:

export const noop = () => {};
export function on(obj, ...args)  { obj?.addEventListener?.(...args); }
export function off(obj, ...args) { obj?.removeEventListener?.(...args); }
export const isBrowser   = typeof window !== 'undefined';
export const isNavigator = typeof navigator !== 'undefined';

on/off are null-safe event (de)registration wrappers used by every event-driven sensor hook (useEvent, useMouse, useScroll, useIdle, useClickAway, …). isBrowser/isNavigator are the environment guards that power the SSR strategy in §3.

src/misc/hookState.ts provides resolveHookState(), the helper that lets hooks accept either a value or a (prev) => next updater function uniformly. It is the basis of the "lazy initializer + functional setter" ergonomics in useGetSet, useList, useCounter, useStateWithHistory, createGlobalState, and others.

export function resolveHookState(nextState, currentState?) {
  if (typeof nextState === 'function') {
    return nextState.length
      ? nextState(currentState)   // (prev) => next
      : nextState();              // () => initial
  }
  return nextState;
}

src/misc/isDeepEqual.ts re-exports fast-deep-equal/react; parseTimeRanges.ts converts TimeRanges into plain objects for the media hooks; types.ts holds the shared PromiseType / FunctionReturningPromise utility types.


3. Server-Side Rendering Strategy

SSR safety is a cross-cutting concern, not a single feature. Because so many hooks touch window, navigator, or document, the library uses a consistent export-time implementation swap: the module decides once, when it loads, whether the relevant browser API exists, and exports either the real hook or a no-op/mock with the same signature.

// pattern: pick the implementation at module load, not per render
export default isBatteryApiSupported ? useBattery : useBatteryMock;
export default isVibrationApiSupported ? useVibrate : noop;
export default isBrowser ? useSearchParam : useSearchParamServer;
export default (isBrowser && hasEventConstructor) ? useLocationBrowser : useLocationServer;
export default (typeof document !== 'undefined') ? useTitle : (_t) => {};

Hooks using this pattern include useBattery, useVibrate, useMediaDevices, useSearchParam, useLocation, useTitle, useMeasure, and useLockBodyScroll. Others guard inline instead — e.g. useLocalStorage and useSessionStorage early-return the initial value plus no-op setters when !isBrowser, and useMedia accepts a defaultState so the server render is deterministic.

useIsomorphicLayoutEffect is the unifying primitive for layout effects:

const useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : useEffect;

This avoids React's "useLayoutEffect does nothing on the server" warning while preserving layout-effect timing in the browser. Internal hooks that measure the DOM (useCss, useMeasure-adjacent code, createGlobalState) build on it.

Note

Because the implementation is chosen at module-load time, the decision is made on the rendering environment that imports the module, not per-call. A hook imported on the server gets the server implementation for that process; the same hook in the browser bundle gets the browser implementation. There is no per-render branching cost.

Warning

A known limitation surfaces with the Next.js App Router: useWindowSize (and other window-reading hooks) can misbehave in React Server Components / the App Router because module-load-time environment detection does not map cleanly onto RSC boundaries (community issue #2532). Call these hooks only inside Client Components ('use client').


4. Design Patterns

4.1 Factory pattern (src/factory/)

Six exports are factories — functions you call once to manufacture a hook or a provider/consumer pair. This is how the library offers configurable or context-bound behavior without a runtime registry.

Factory Produces Backed by
createHTMLMediaHook(tag) useAudio / useVideo shared media element controller (internal)
createGlobalState(initial) a provider-less shared-state hook module-level store + setter list
createReducer(...middleware) a useReducer hook with Redux middleware useRef + composed middleware chain
createReducerContext(reducer, init) [useCtx, Provider, Context] useReducer + createContext
createStateContext(init) [useCtx, Provider, Context] useState + createContext
createBreakpoint(map) a named-breakpoint hook resize listener + sorted thresholds
createMemo(fn) a memoizing hook useMemo keyed by args

createHTMLMediaHook is the most substantial: a single ~240-line factory in src/factory/createHTMLMediaHook.ts produces both useAudio and useVideo. It owns a ref to the media element, wires the full set of media events (onPlay, onPause, onTimeUpdate, onVolumeChange, onDurationChange, onProgress, …) through a wrapEvent helper that preserves any user-supplied handlers, and exposes an imperative controls object (play, pause, seek, volume, mute, unmute). It guards .play() with a lockPlay flag to work around the Chromium "play() request was interrupted" promise bug.

Note

Two factories exist in source but are not exported from src/index.ts: createRouter and createRenderProp. They are internal/experimental and should not be relied on as public API.

4.2 Getter-based state (escaping stale closures)

Several hooks return a getter instead of (or alongside) a value, so async callbacks always read the freshest state without re-subscribing. useGetSet, useGetSetState, and useMountedState are the canonical examples; useLatest provides the ref-based variant. This pattern is the library's answer to the React "stale closure" problem in timers and async work.

4.3 Compare-effect family

useCustomCompareEffect is the base; useDeepCompareEffect and useShallowCompareEffect specialize it with fast-deep-equal and fast-shallow-equal respectively. All three carry development-only console.warn guards that steer users back to React.useEffect when dependencies are absent or all-primitive — a deliberate "pit of success" design choice.

4.4 Alias hooks

A few exports are pure aliases, kept for naming ergonomics: useBoolean → useToggle, useNumber → useCounter. They add no behavior; they re-export the underlying module.


5. Build, Distribution & Configuration

This library has no application configuration. Its "configuration surface" is its build pipeline and package.json metadata.

5.1 Output formats

Per the multi-format-library pattern, the package ships two builds from the same TypeScript source:

Field Path Format Built by
main lib/index.js CommonJS tsc (build:cjs)
module esm/index.js ES Modules tsc -m esNext --outDir esm (build:es)
types / typings lib/index.d.ts TypeScript decls tsc

"sideEffects": false permits aggressive tree-shaking, and "files": ["lib/", "esm/"] limits the published tarball to the two builds.

5.2 npm scripts (the real operational surface)

AST extraction does not capture npm scripts, so the authoritative task list comes straight from package.json:

Script Command Purpose
start yarn storybook Launch the Storybook dev/demo server.
test jest --maxWorkers 2 Run the browser-jsdom test suite.
test:ssr jest --config ./jest.config.node.ts Run tests under a Node/SSR environment.
test:watch jest --watch Watch-mode tests.
test:coverage jest --coverage Coverage report.
lint eslint "{src,tests,stories}/**/*.{ts,tsx}" Lint all source.
lint:fix yarn lint --fix Autofix lint issues.
lint:types tsc --noEmit Type-check without emitting.
build:cjs tsc Build the CommonJS output to lib/.
build:es tsc -m esNext --outDir esm Build the ESM output to esm/.
build yarn build:cjs && yarn build:es Full dual build.
clean rimraf lib storybook-static esm Remove build artifacts.
storybook start-storybook -p 6008 Storybook on port 6008.
storybook:build build-storybook Static Storybook build.
release semantic-release Automated versioned release.

5.3 Toolchain pins

The repo pins its toolchain via volta (node 14.21.3, yarn 1.22.22) and uses husky Git hooks: pre-commit runs lint-staged (eslint --fix on staged TS), and pre-push runs lint && clean && build && test. Releases are fully automated through semantic-release (changelog + npm + git plugins) on the master and next branches.

Tip

The only repository NODE_ENV consumer in source is the dev/prod branching in hooks (e.g. the media-element error logging and the compare-effect warnings). There is no environment-variable configuration to manage at runtime — these guards are compiled away by the consumer's bundler in production builds.


6. Dependencies

The library is deliberately composed of small, single-purpose runtime dependencies. Document the integration, not the libraries themselves.

Dependency Used by Why
js-cookie useCookie Read/write browser cookies.
copy-to-clipboard useCopyToClipboard Cross-browser clipboard write.
screenfull useFullscreen Cross-browser Fullscreen API wrapper.
nano-css useCss Runtime CSS-in-JS (CSSOM/VCSSOM addons).
fast-deep-equal useDeepCompareEffect, useBattery Deep equality.
fast-shallow-equal useShallowCompareEffect Shallow equality.
resize-observer-polyfill useMeasureDirty ResizeObserver fallback.
@xobotyi/scrollbar-width useScrollbarWidth Measure scrollbar width.
set-harmonic-interval useHarmonicIntervalFn Synchronized intervals.
throttle-debounce useIdle Throttling.
ts-easing useTween Easing functions.
react-universal-interface useScratch (render-prop) Render-prop/children API.
tslib (all) TypeScript runtime helpers.
@types/js-cookie useCookie Type definitions.

Optional peer integrations (NOT bundled, hooks not exported by default): keyboardjs for useKeyboardJs, rebound for useSpring. Consumers who want these must install the peer and import the hook directly from its source path.


7. Security Analysis

react-use runs entirely in the consumer's browser context and has no server, no network calls of its own, and no secret handling. The security surface is therefore (a) the data hooks read/write in browser storage, and (b) transitive dependency risk.

7.1 Browser storage and cookies

useLocalStorage, useSessionStorage, and useCookie read and write client-side storage. They perform no encryption or sanitization — that is the caller's responsibility. Do not store secrets, tokens you cannot rotate, or PII in localStorage/sessionStorage via these hooks; the values are readable by any script on the page.

The storage hooks are defensively wrapped in try/catch to survive private-mode and storage-restricted environments (where localStorage access throws), and fall back to the initial value rather than crashing.

Caution

The pinned js-cookie@^2.2.1 (used by useCookie) is affected by a reported prototype-pollution advisory (community issue #2704, CVE-2026-46625). If your application uses useCookie, audit the resolved js-cookie version and force a patched release via your package manager's override/resolution mechanism, or avoid useCookie and use a vetted cookie library directly.

7.3 DOM/event hooks

Hooks that attach global listeners (useEvent, useKey, useIdle, useClickAway, usePageLeave, drag/drop hooks) register on window/document and clean up on unmount via the on/off helpers. They do not evaluate or inject untrusted strings. useCss generates class names from a counter (no user input in the selector), so it does not introduce a CSS-injection vector beyond the style object you pass it.

7.4 General guidance

  • Treat every value flowing through storage/cookie hooks as untrusted on read.
  • Pin and audit transitive dependencies; the library's * peer range means React version safety is delegated to the consumer.
  • Production builds strip the dev-only console diagnostics via NODE_ENV, so no internal state leaks to end-user consoles in production.

8. Testing & Quality

Aspect Detail
Runner Jest (jest --maxWorkers 2)
Browser env jsdom (default config)
SSR env Node config (jest.config.node.ts, run via test:ssr)
Component testing @testing-library/react, @testing-library/react-hooks
Mocks @shopify/jest-dom-mocks, jest-localstorage-mock, raf-stub
Lint ESLint + @typescript-eslint, Prettier, eslint-plugin-react-hooks
Type check tsc --noEmit (lint:types)

The dual browser/SSR test configuration is notable: it directly exercises the SSR-swap strategy from §3, so regressions in environment guards are caught by the test:ssr suite. Tests mirror the src/ tree under tests/, and the raf-stub / jest-localstorage-mock setup lets the timer and storage hooks be tested deterministically.

Tip

When contributing a hook that touches a browser-only API, add both a browser test and an SSR test. The SSR path (mock/no-op export) is easy to break and is only covered by yarn test:ssr.


9. Maintenance & Operational Status

Important

Issue #2703 ("Archiving react-use because it's unmaintained") is open, and related issues note the published version lags the repository (#2689) and that several long-standing bugs remain open. The library is best understood as stable and in maintenance mode rather than actively evolving.

Open issues worth tracking before adopting or upgrading:

Issue Area Impact
#2700 useDeepCompareEffect Throws when comparing objects made with Object.create(null).
#2684 useCss / nano-css Vite import-analysis failure resolving nano-css/addon/vcssom.
#2675 useScrolling Does not clean up its scroll-end timer.
#2612 useIntersection Warning under React 19.
#2532 useWindowSize Does not work under the Next.js App Router.
#2227 useDeepCompareEffect Spurious warning for null/undefined non-primitive values.
#2704 useCookie / js-cookie Transitive CVE (see §7.2).

Migration considerations

  • If you depend on useCss, validate it against your bundler (Vite users: see #2684) or vendor a thin CSS-in-JS alternative.
  • For React 19 adopters, watch useIntersection (#2612) and the general App-Router caveats; prefer calling sensor hooks in Client Components only.
  • The * peer-dependency range means the library will install against any React version, but it is validated against React 17 in CI (react@17.0.2 in devDependencies). Treat React 18/19 support as community-verified rather than CI-guaranteed.

10. Contributing

The repository is a standard TypeScript library workflow:

# 1. Install (Volta pins node 14.21.3 / yarn 1.22.22)
yarn install

# 2. Run the demo/dev environment
yarn start            # Storybook on http://localhost:6008

# 3. Make your change under src/, add a story + tests

# 4. Verify locally (mirrors the pre-push hook)
yarn lint
yarn lint:types
yarn build
yarn test
yarn test:ssr         # don't skip this for browser-API hooks

Conventions to follow when adding a hook:

  • [ ] One hook per file under src/, named useThing.ts(x).
  • [ ] Re-export it by name from src/index.ts (the public barrel).
  • [ ] Guard any window/navigator/document access with isBrowser / isNavigator and provide an SSR-safe export (real-vs-mock swap).
  • [ ] Reuse on/off from src/misc/util.ts for event listeners and clean them up on unmount.
  • [ ] Use resolveHookState for setters that should accept both values and updater functions.
  • [ ] Add a Storybook story under stories/ (this is the de-facto demo doc) and a test under tests/, including an SSR test where relevant.
  • [ ] Gate development diagnostics behind process.env.NODE_ENV !== 'production'.

Commits feed semantic-release, so follow the Conventional Commits format (commitizen is configured via git-cz) — the release type and changelog are derived from commit messages automatically.

Note

Do not export experimental factories (createRouter, createRenderProp) from the barrel, and remember that peer-dependency-gated hooks (useKeyboardJs, useSpring) are intentionally omitted from src/index.ts.


11. Quick Reference

Need Where
What is exported src/index.ts (the only authoritative list)
Shared helpers src/misc/util.ts, src/misc/hookState.ts
Hook factories src/factory/
SSR strategy isBrowser/isNavigator guards + export-time impl swap (§3)
Build outputs lib/ (CJS), esm/ (ESM), types in lib/index.d.ts
Run the demo yarn start → Storybook :6008
Run tests yarn test (browser) + yarn test:ssr (Node)
Release semantic-release on master/next
License Unlicense (public domain)
Maintenance status In maintenance mode; see §9

Key facts (from source)

  • Package name/version: react-use 17.6.0; entry src/index.ts.
  • Peer deps: react, react-dom (range *); React 16.8+ required for Hooks.
  • Dual build via tsc: lib/ (CJS) + esm/ (ESM); "sideEffects": false.
  • ~100 hooks + 6 exported factories; 2 factories (createRouter, createRenderProp) and 2 hooks (useKeyboardJs, useSpring) are intentionally not exported.
  • No application config, no runtime env vars, no server component.