mrkeyoor.com_
Tue 22 Sept 22:35 UTC
npmWeb Frontendupdated 22 Sept 2026

usehooks-ts review

usehooks-ts 3.1.1 is a set of typed React hooks for browser-backed component behavior. Its catalog includes local and session storage, media and screen queries, window and element sizing, outside clicks, event listeners, clipboard access, timers, debouncing, dark mode, script loading, and small state helpers. It does not fetch application data or manage global state. The package publishes ESM and CommonJS entries, bundled declarations, and sideEffects:false metadata. Version 3.1.1 adds React 19 to the peer range; version 3.1.0 added remove functions to the localStorage and sessionStorage hooks.

Verdict

usehooks-ts 3.1.1 installed in 0.8 seconds, used 1 MB across 3 packages, and measured 8.3 KB gzipped for the full namespace in our sandbox. Add it when several browser hooks earn their place and your bundler tree-shakes named imports; keep one-off hooks local and treat SSR defaults as API decisions.

We installed it

Lab card: what happened when we installed usehooks-tsScreenshot of usehooks-ts documentation
Install✓ · 0.8s3 packages on disk · 1 MB
ImportESM import works · require() works · ESM package with exports map
Browser8.3 KBgzipped (24 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does usehooks-ts install cleanly?

Yes. In a fresh container with an empty cache, npm install usehooks-ts finished in 0.8s, leaving 3 packages and 1 MB on disk. npm audit reported no known vulnerabilities.

How much does usehooks-ts add to a browser bundle?

8.3 KB gzipped (24 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does usehooks-ts work with both ESM and CommonJS?

Yes. Both import 'usehooks-ts' and require('usehooks-ts') worked in Node 22 in our run. The package is published as ESM with an exports map.

Does usehooks-ts include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

usehooks-ts or react-use: which should you use?

react-use: Use it when a much larger catalog of sensors, lifecycle helpers, and async hooks is the requirement. usehooks-ts 3.1.1 installed in 0.8 seconds, used 1 MB across 3 packages, and measured 8.3 KB gzipped for the full namespace in our sandbox.

When should you not use usehooks-ts?

Only one five-line hook is needed. Local code is easier to audit than a collection whose API and release cycle you otherwise do not use.

API stability3/5The 3.1 line adds features without reshaping established hooks: storage hooks gained remove functions and 3.1.1 widened the React peer range to version 19. The preceding 3.0 major removed deprecated hooks and signatures, changed type visibility, and moved the workspace to ESM. Current call shapes are documented and typed, but the major-version history shows that deprecated convenience APIs are eventually deleted rather than kept indefinitely.
Docs5/5The README enumerates every shipped hook and links to a dedicated page with signature, options, return type, source, and examples. SSR-sensitive hooks document initializeWithValue, while DOM hooks name their underlying browser APIs. The changesets record removals, storage behavior, observer fixes, and React compatibility. Readers still need framework-specific knowledge for client boundaries and realistic layout tests, which cannot be solved by a hook reference alone.
Maintenance4/5The repository was pushed on August 25, 2026, is unarchived, has 7,851 stars, and GitHub reports 127 open issues and pull requests. The latest npm package, 3.1.1, dates to February 2025 and only adds React 19 peer support. Source and documentation activity therefore run ahead of the published release cadence. That is acceptable for settled browser wrappers, but teams waiting on a fix should inspect whether it has reached npm.
Ecosystem5/5The npm endpoint counted 5,514,281 downloads in the latest week. Version 3.1.1 peers with every React line from 16.8 through 19, publishes import and require targets, bundles declarations, and marks itself side-effect-free. Most hooks wrap stable Web Platform APIs rather than vendor services, which limits adapter needs. The package remains React-specific, and server-component frameworks require client boundaries around every consuming component.

Use it if

  • Several components need the same tested storage, media, observer, timer, or DOM-listener patterns.
  • React versions from 16.8 through 19 must share one hook package with explicit peer support.
  • TypeScript signatures and dedicated examples are worth more than owning several short local hooks.
  • The team will set each browser-reading hook's SSR fallback and hydration behavior deliberately.
Skip it if

Setup reality

We installed usehooks-ts 3.1.1 in 0.8 seconds in a fresh Node 22 container. Three packages used 1 MB on disk, and npm audit found 0 known vulnerabilities. The package has one direct dependency and one React peer, with 264 KB unpacked. It requires Node 16.15 or newer, is ESM with an exports map, includes TypeScript declarations, and worked with require() and ESM import in our checks.

There are no credentials or configuration files. React is a peer and must already be installed; version 3.1.1 accepts React 16.8 through 19. In Next.js App Router, a component importing these hooks needs a client boundary. Web APIs such as localStorage, matchMedia, Clipboard, ResizeObserver, and IntersectionObserver are absent during server rendering and incomplete in jsdom, so SSR fallbacks and test mocks are part of setup.

Storage, media, window, screen, and dark-mode hooks can read the browser on their first client render. Set initializeWithValue:false when the server must render the same fallback, then expect the value to change after mount. Storage serialization is JSON by default. Date, Map, and Set need paired serializer and deserializer functions. Clipboard writes require a secure context and usually a user gesture; observer hooks need actual layout in browser tests.

Our import-all browser build was 24 KB minified and 8.3 KB gzipped. The package is marked sideEffects:false, so named imports should let a production bundler remove unused hooks; measure your output because a namespace import keeps more code. useDebounceCallback brings lodash.debounce behavior, including cancel and flush. Hooks register and clean up listeners, but your callback identity, SSR default, observer threshold, and interval delay still determine rerenders and hydration changes.

Patterns

Keep typed filters in local storage persist-local-state

import { useLocalStorage } from 'usehooks-ts';

const [filters, setFilters, removeFilters] = useLocalStorage(
  'product-filters',
  { query: '', inStock: false },
);
setFilters((current) => ({ ...current, inStock: true }));

JSON is the default encoding. The third tuple item removes the key and was added in version 3.1.

Match the server render before reading storage defer-storage-read

const [theme, setTheme] = useLocalStorage(
  'theme',
  'system',
  { initializeWithValue: false },
);

The initial server and client values remain system; the stored value arrives after mount and can change visible output once.

Restore a Date instead of a string serialize-date

const [lastSeen, setLastSeen] = useLocalStorage<Date>(
  'last-seen',
  new Date(0),
  {
    serializer: (value) => value.toISOString(),
    deserializer: (raw) => new Date(raw),
  },
);

Default JSON parsing does not recreate Date, Map, or Set instances. Serializer and deserializer must agree on the stored shape.

Read a responsive breakpoint after mount track-media-query

import { useMediaQuery } from 'usehooks-ts';

const narrow = useMediaQuery('(max-width: 48rem)', {
  defaultValue: false,
  initializeWithValue: false,
});

initializeWithValue:false avoids a server/client first-render mismatch, but the layout can update after matchMedia runs.

Close a panel on an outside press handle-outside-pointer

const panelRef = useRef<HTMLDivElement>(null);
useOnClickOutside(panelRef, () => setOpen(false), 'mousedown');

return <div ref={panelRef}>Panel content</div>;

mousedown is the default event. The hook can accept multiple refs and touch or focus event names.

Close on the Escape key listen-window-key

import { useEventListener } from 'usehooks-ts';

useEventListener('keydown', (event) => {
  if (event.key === 'Escape') setOpen(false);
});

Without a target ref, the listener attaches to window and is removed when the component unmounts.

Attach a listener to a ref target listen-element-focus

const buttonRef = useRef<HTMLButtonElement>(null);
useEventListener('focus', () => setFocused(true), buttonRef);

return <button ref={buttonRef}>Save</button>;

Pass the RefObject, not buttonRef.current, so the hook can follow the element after mount.

Delay search until typing pauses debounce-search

const search = useDebounceCallback(
  (query: string) => runSearch(query),
  300,
  { trailing: true, maxWait: 1_200 },
);

return <input onChange={(event) => search(event.target.value)} />;

The debounced function exposes cancel, flush, and isPending. Cancel it when stale work must not fire after navigation.

Render content after it approaches the viewport observe-intersection

const { ref, isIntersecting } = useIntersectionObserver({
  threshold: 0.25,
  rootMargin: '200px',
  freezeOnceVisible: true,
});

return <section ref={ref}>{isIntersecting ? <Results /> : null}</section>;

freezeOnceVisible stops updates after the first hit. jsdom tests need an IntersectionObserver mock.

Track a panel with ResizeObserver observe-size

const panelRef = useRef<HTMLDivElement>(null);
const { width = 0, height = 0 } = useResizeObserver({
  ref: panelRef,
  box: 'border-box',
});

return <div ref={panelRef}>{width} x {height}</div>;

ResizeObserver reports browser layout, which jsdom does not calculate. The onResize option can avoid a React render for every size change.

Pause an interval without changing hook order toggle-interval

useInterval(
  () => setSeconds((value) => value + 1),
  running ? 1_000 : null,
);

A null delay clears the timer. Functional state updates keep each tick independent of a captured seconds value.

Report a failed clipboard write copy-clipboard

const [copiedText, copy] = useCopyToClipboard();

async function copyLink() {
  const copied = await copy(location.href);
  if (!copied) setError('Copy failed');
}

Clipboard writes generally need HTTPS or localhost plus a user action. The hook resolves false when the browser write fails.

Alternatives

PackageRegistryPick it when
react-usenpmUse it when a much larger catalog of sensors, lifecycle helpers, and async hooks is the requirement.
ahooksnpmUse it for a broad application toolkit that includes request, polling, DOM, and state utilities.
@uidotdev/usehooksnpmUse it for a smaller modern collection with tutorial-led examples.

More web frontend guides

postcss · react · react-dom · tailwindcss · htmlparser2 · tailwind-merge · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.