mrkeyoor.com_
Sat 08 Aug 21:01 UTC
npmWeb Frontendupdated 08 Aug 2026

usehooks-ts

usehooks-ts is a typed collection of React hooks for browser state and common component behavior. Its 34 exports cover local and session storage, media queries, element and window sizing, event listeners, outside clicks, clipboard access, timers, debouncing, dark mode, script loading, and small state helpers. It is not a state manager or component kit; each named export wraps a focused React or Web Platform pattern and the package is marked side-effect-free for tree shaking.

Verdict

A good middle-sized hook collection when a project needs several browser utilities and will treat SSR options deliberately. Do not add it for one five-line hook, and do not mistake it for a data-fetching or state-management layer.

API stability3/5The project follows semantic versioning and the v3.1 changelog shows additive and patch-level changes, including storage remove functions and React 19 peer support. The v3.0 release also removed previously deprecated hooks and signatures, renamed or hid types, and changed packaging. Current consumers have a clear API, but a major upgrade can require real source edits.
Docs5/5The README lists every available hook and links each one to a dedicated usehooks-ts.com page. The shipped declarations add parameter defaults, return shapes, examples, browser API links, and explicit SSR notes such as initializeWithValue: false. The changelog is unusually useful because it records removed hooks, signature changes, storage serialization limits, and compatibility fixes.
Maintenance4/5The repository was pushed on July 30, 2026 and is not archived, with active source, tests, generated docs, and changeset tooling in the monorepo. The latest npm release, 3.1.1, dates to February 2025, and GitHub reports 125 open issues and pull requests together. Development is visible, but published-package cadence is slower than the repository activity suggests.
Ecosystem5/5The npm endpoint reports 5,042,950 downloads for the measured week, GitHub reports 7,852 stars, and the peer range spans every React line from 16.8 through 19. The package ships both import and require targets with declarations and supports standard browser APIs rather than proprietary integrations, so it fits most React build stacks without adapter packages.

Use it if

  • You need several ordinary browser hooks and want consistent TypeScript signatures, cleanup, and tests instead of maintaining copies
  • Your app supports React 16.8 through 19 and needs one package whose peer range explicitly covers all of them
  • You use server rendering and value the documented initializeWithValue switches on storage, media, screen, and window hooks
  • You want ESM and CommonJS entry points with declarations included and named exports that bundlers can tree-shake
Skip it if

Setup reality

Install usehooks-ts alongside React; version 3.1.1 declares React ^16.8, 17, 18, 19, or the 19 release candidate as a peer and requires Node 16.15 or newer for its toolchain compatibility. It includes TypeScript declarations, ESM import and CommonJS require targets, is marked sideEffects: false, and has one runtime dependency, lodash.debounce. There is no provider or config file. The work is at component boundaries. Every consumer is a client component in frameworks such as Next.js App Router, so add the client directive at the top of the importing file. Browser-backed hooks often read on first render by default. For SSR, pass initializeWithValue: false to useLocalStorage, useSessionStorage, useReadLocalStorage, useMediaQuery, useWindowSize, useScreen, useDarkMode, or useTernaryDarkMode, then accept that the server's fallback may change after hydration. Storage values are JSON by default; version 2.11 removed automatic Date, Map, and Set support, so provide matching serializer and deserializer functions for those values. Clipboard access needs a secure context and user permission. IntersectionObserver and ResizeObserver need browser support or test mocks, and jsdom does not implement their layout behavior. useOnClickOutside expects refs to mounted nodes, while useEventListener's target is a RefObject, not ref.current. Version 3 removed deprecated hooks and signatures, so check the changelog before copying v2 examples from blogs.

Patterns

Store typed state in localStoragepersist-local-state

import { useLocalStorage } from 'usehooks-ts';

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

setFilters(current => ({ ...current, inStock: true }));

The default serializer is JSON-based. removeFilters() deletes the key and was added in v3.1.

Delay localStorage reads during SSRhydrate-storage-safely

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

The server and first client render use the initial value; the stored value is read after mounting, so the UI may update once.

Persist a value with custom serializationserialize-date

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

v3 does not automatically restore Date, Map, or Set instances; serializer and deserializer must agree.

Track a responsive media querymatch-media-query

import { useMediaQuery } from 'usehooks-ts';

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

Disabling the initial browser read avoids server hydration mismatch, but responsive content changes after mount.

Close a dialog on outside interactionhandle-outside-click

import { useRef } from 'react';
import { useOnClickOutside } from 'usehooks-ts';

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

return <div ref={dialogRef}>Dialog content</div>;

The default event is mousedown. The hook also accepts an array of refs and touch or focus event types.

Attach a typed window event listenerlisten-window-event

import { useEventListener } from 'usehooks-ts';

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

Omit the element argument for window events; cleanup and the latest handler reference are managed by the hook.

Listen on a referenced elementlisten-element-event

const buttonRef = useRef<HTMLButtonElement>(null);

useEventListener('focus', () => setFocused(true), buttonRef);
return <button ref={buttonRef}>Save</button>;

Pass the RefObject itself, not buttonRef.current, so the hook can follow the mounted target.

Debounce a search callbackdebounce-search

import { useDebounceCallback } from 'usehooks-ts';

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

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

The returned function also exposes cancel(), flush(), and isPending(); calls can return undefined before the first invocation.

Load content when it first becomes visibleobserve-intersection

import { useIntersectionObserver } from 'usehooks-ts';

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

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

freezeOnceVisible stops observer updates after the first intersection; tests need an IntersectionObserver mock.

Track an element with ResizeObserverobserve-element-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>;

Passing onResize avoids re-rendering on each size change; ResizeObserver support or a polyfill is required.

Pause and resume an intervalrun-interval

import { useInterval } from 'usehooks-ts';

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

A null delay clears the interval without changing hook call order; the callback always sees current React state when using a functional update.

Copy text and report successcopy-clipboard

import { useCopyToClipboard } from 'usehooks-ts';

const [copiedText, copy] = useCopyToClipboard();

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

Clipboard writes usually require HTTPS or localhost and a user gesture; the function resolves false rather than throwing the browser error to your component.

Alternatives

PackageRegistryPick it when
react-usenpmChoose it when breadth matters more and you need a much larger catalog including sensors, lifecycle, and async utilities
ahooksnpmChoose it for enterprise React apps that want richer request, polling, DOM, and state hooks in one library
@uidotdev/usehooksnpmChoose it when you prefer a smaller modern hook set paired with tutorial-style examples