Compare

How react-tils compares

The same 10 everyday tasks in seven React hook libraries, rendered on a real server, hydrated in a browser, checked for leaked listeners, and measured. Everything on this page is generated by a script you can run yourself.

  • react-tils passes every check on all 10 of 10 tasks. 5 of the 6 other libraries throw on the server or cause a hydration mismatch on at least one, with default settings.
  • Smallest hook on 4 of 10 tasks. @uidotdev/usehooks is often a few bytes smaller, but its hooks for browser state are client-only: they throw during server rendering.
  • Only the hooks you import ship to your users. The whole-library sizes are listed at the end for reference.

Server rendering

Each hook is rendered with renderToString in plain Node, then the HTML is hydrated in a desktop-sized browser environment, then mounted and unmounted under StrictMode to find listeners left behind. Hover a mark for the error.

Taskreact-tils@uidotdev/usehooksusehooks-ts@mantine/hooksreact-use@reactuses/coreahooks
Media query✓useMediaQuery✕useMediaQuery!useMediaQuery✓useMediaQuery!useMedia!useMediaQuery—
Window size✓useWindowSize✓useWindowSize!useWindowSize✓useViewportSize!useWindowSize✓useWindowSize—
Window scroll✓useWindowScroll✓useWindowScroll—✓useWindowScroll✓useWindowScroll✓useWindowScroll✓useScroll
Element in view✓useInView✓useIntersectionObserver✓useIntersectionObserver✓useInViewport✓useIntersection✓useIntersectionObserver✓useInViewport
localStorage state✓useLocalStorage✕useLocalStorage✓useLocalStorage✓useLocalStorage✓useLocalStorage✓useLocalStorage✓useLocalStorageState
Click outside✓useClickOutside✓useClickAway✓useOnClickOutside✓useClickOutside✓useClickAway✓useClickOutside✓useClickAway
Keyboard shortcut✓useHotkey——✓useHotkeys✓useKey—✓useKeyPress
Debounced value✓useDebouncedValue✓useDebounce✓useDebounceValue✓useDebouncedValue✓useDebounce✓useDebounce✓useDebounce
Debounced callback✓useDebouncedCallback—✓useDebounceCallback✓useDebouncedCallback—✓useDebounceFn✓useDebounceFn
Online status✓useOnline✕useNetworkState—✓useNetwork!useNetworkState!useOnline!useNetwork
Passes every check10 / 105 / 85 / 710 / 106 / 97 / 97 / 8
  • ✓ Renders on the server, hydrates without a mismatch, and leaves no listeners behind.
  • ! Hydration mismatch: the browser's first render differs from the server HTML, so React throws the server HTML away and re-renders.
  • ✕ Throws during server rendering, so the page fails unless you avoid rendering it on the server.
  • — No equivalent hook.

With each hook's default settings, as most code calls it. Some mismatches can be avoided with an option: initializeWithValue: false in usehooks-ts, or a default state in react-use's useMedia and @reactuses/core's useMediaQuery.

Size per hook

Bytes after minifying and compressing with brotli, React excluded: what a bundler adds to your app for that one hook. The smallest in each row is highlighted.

Taskreact-tils@uidotdev/usehooksusehooks-ts@mantine/hooksreact-use@reactuses/coreahooks
Media query150 B181 B968 B256 B208 B318 B—
Window size194 B159 B1373 B213 B414 B861 B—
Window scroll191 B286 B—278 B388 B4451 B622 B
Element in view268 B234 B1184 B209 B201 B4208 B702 B
localStorage state389 B362 B1392 B791 B394 B5704 B1101 B
Click outside214 B184 B994 B294 B269 B4325 B576 B
Keyboard shortcut611 B——710 B382 B*—2167 B
Debounced value128 B120 B1252 B284 B252 B*1249 B1484 B
Debounced callback309 B—1167 B478 B—1200 B1446 B
Online status134 B377 B—365 B375 B425 B410 B
Whole library12.0 kB4.1 kB4.7 kB21.3 kB20.3 kB23.9 kB22.9 kB
  • * Keyboard shortcut, react-use: One key; no modifiers like mod+k, no sequences.
  • * Debounced value, react-use: A debounced effect: you set state in it yourself.
  • Whole-library sizes cover different numbers of hooks; with tree shaking, only the hooks you import count.

How this was measured

  • Versions: react-tils 0.1.5, @uidotdev/usehooks 2.4.1, usehooks-ts 3.1.1, @mantine/hooks 9.7.1, react-use 17.6.1, @reactuses/core 6.5.9, ahooks 3.10.0. Measured on 2026-10-12.
  • Server: renderToString in a Node environment with no DOM at all, as on a real server.
  • Hydration: the server HTML is hydrated with hydrateRoot in happy-dom with a 1024×768 window, online, and an empty localStorage. A mismatch is any recoverable hydration error React reports.
  • Leaks: the hook is mounted and unmounted under StrictMode; any event listener added and not removed fails it. These are the same checks react-tils runs on its own hooks.
  • Size: each hook imported on its own and bundled with size-limit, React excluded, so every library is measured the same way.
  • Not measured here: whether a hook shows the right value on its first frame after client-side navigation, and React Compiler support.

The script and every hook call are in compare/. Run it with pnpm compare. Spotted an unfair call? Open an issue.