@uidotdev/usehooks, ahooks, and react-use are all libraries that provide reusable React custom hooks to simplify common frontend tasks like state management, side effects, browser APIs, and performance optimizations. These packages help developers avoid reinventing the wheel by offering well-tested, composable hooks that follow React best practices. While they share overlapping functionality, they differ significantly in API design philosophy, TypeScript support, feature scope, and target use cases.
When building React applications, custom hooks are essential for encapsulating logic, managing side effects, and interacting with browser APIs. The three libraries — @uidotdev/usehooks, ahooks, and react-use — each offer curated sets of hooks, but they take different approaches in design, scope, and developer experience. Let’s compare them through real-world engineering lenses.
@uidotdev/usehooks focuses on clarity and education. Each hook is small, well-documented, and often accompanied by live examples. It avoids complex abstractions and sticks to solving one problem per hook.
// @uidotdev/usehooks: simple, readable implementation
import { useLocalStorage } from '@uidotdev/usehooks';
function MyComponent() {
const [name, setName] = useLocalStorage('name', 'Anonymous');
return <input value={name} onChange={e => setName(e.target.value)} />;
}
ahooks emphasizes robustness and type safety, especially in large-scale applications. It includes advanced features like request cancellation, debouncing control, and detailed loading states — all with strong TypeScript definitions.
// ahooks: rich options and precise typing
import { useRequest } from 'ahooks';
function UserProfile({ id }: { id: string }) {
const { data, loading, error } = useRequest(() => fetchUser(id), {
manual: false,
debounceWait: 300,
});
// data is strongly typed based on fetchUser's return type
}
react-use offers the widest variety of hooks — over 100 — including many for niche browser APIs (like useBattery, useSpeechRecognition). Some hooks are more experimental, and the API can feel less consistent across the library.
// react-use: broad coverage, including uncommon APIs
import { useClickAway, useMedia } from 'react-use';
function Modal() {
const ref = useRef();
const isMobile = useMedia('(max-width: 768px)');
useClickAway(ref, () => closeModal());
return <div ref={ref}>{isMobile ? 'Mobile View' : 'Desktop View'}</div>;
}
All three provide hooks for localStorage, but their APIs differ in flexibility and error handling.
@uidotdev/usehooks keeps it simple: just get and set.
import { useLocalStorage } from '@uidotdev/usehooks';
const [value, setValue] = useLocalStorage('key', 'default');
ahooks gives you full control, including serialization customization and error callbacks.
import { useLocalStorageState } from 'ahooks';
const [value, setValue] = useLocalStorageState('key', {
defaultValue: 'default',
serializer: v => JSON.stringify(v),
deserializer: v => JSON.parse(v!),
});
react-use uses a similar simple signature but supports functional updates.
import { useLocalStorage } from 'react-use';
const [value, setValue] = useLocalStorage('key', 'default');
// setValue(prev => prev + ' updated') works
@uidotdev/usehooks does not include a dedicated data-fetching hook. You’d typically pair it with fetch + useEffect or a separate library like SWR.
ahooks provides useRequest — a powerful, SWR-inspired hook with built-in polling, retry, pagination, and cache management.
const { data, loading, run } = useRequest(getData, {
pollingInterval: 5000,
ready: !!userId, // only run when userId exists
});
react-use includes basic fetching via useAsync, but it’s minimal and lacks advanced features like caching or deduplication.
import { useAsync } from 'react-use';
const { value, loading } = useAsync(() => fetchData(), []);
For serious data requirements, ahooks stands out; the others expect you to bring your own solution.
All three handle timing utilities, but with different ergonomics.
@uidotdev/usehooks offers useDebounce that returns a debounced value:
const debouncedSearch = useDebounce(searchTerm, 500);
useEffect(() => {
if (debouncedSearch) performSearch(debouncedSearch);
}, [debouncedSearch]);
ahooks provides useDebounceFn, which returns a debounced function you can call directly:
const { run: debouncedSearch } = useDebounceFn(
(term) => performSearch(term),
{ wait: 500 }
);
// Call debouncedSearch(term) in onChange
react-use also uses the function-returning pattern:
const debounced = useDebounce(() => performSearch(searchTerm), 500);
// But note: this creates a new debounced fn on every render unless memoized
ahooks’s approach is more predictable in component re-renders due to stable function identity and explicit control.
@uidotdev/usehooks ships with TypeScript types, but they’re basic and inferred rather than explicitly designed.
ahooks is built with TypeScript from the ground up. Every hook has precise generics, and the library enforces strict type contracts (e.g., useRequest infers response type from the async function).
react-use has TypeScript support, but some hooks have loose or any-typed internals, requiring more manual type assertion in complex scenarios.
If your team relies heavily on TypeScript for correctness, ahooks provides the strongest guarantees.
@uidotdev/usehooksahooksreact-useuseNetwork, useGeolocation).Despite differences, all three libraries:
window access in render phase).Example of safe SSR-compatible hook usage across all three:
// All libraries avoid accessing window during render
// They use useEffect or lazy initialization internally
const width = useWindowSize().width; // works in SSR without crashing
| Feature | @uidotdev/usehooks | ahooks | react-use |
|---|---|---|---|
| Philosophy | Simple, educational | Robust, enterprise-ready | Broad, experimental |
| TypeScript Quality | Basic | Excellent | Moderate |
| Data Fetching | Not included | Full-featured (useRequest) | Minimal (useAsync) |
| Hook Count | ~20 | ~80 | ~100+ |
| API Consistency | High | Very high | Moderate |
| Best For | Learning, small apps | Large apps, TypeScript | Prototyping, niche APIs |
These libraries aren’t mutually exclusive — you could even mix them selectively. But for architectural consistency, choose one based on your team’s priorities: simplicity (@uidotdev/usehooks), reliability (ahooks), or breadth (react-use). In most professional settings where maintainability and type safety matter, ahooks tends to offer the best balance of power and predictability.
Choose react-use if you need the broadest collection of utility hooks covering diverse browser APIs and edge cases, and you’re comfortable with a more experimental or flexible API style. It works well for rapid prototyping or projects that require niche functionality not found in smaller libraries, though you may need to verify stability for mission-critical features.
Choose @uidotdev/usehooks if you value minimal, focused hooks with clear documentation and educational examples. It’s ideal for teams that prefer lightweight utilities without complex configuration or opinionated patterns, especially when building straightforward applications where simplicity and readability are prioritized over advanced features.
Choose ahooks if you're working in a TypeScript-heavy codebase and need robust, production-ready hooks with strong type safety and comprehensive lifecycle management. It’s particularly well-suited for enterprise applications built with Ant Design or other Alibaba ecosystem tools, where consistency, reliability, and deep integration with modern React patterns are critical.
libreact.
npm i react-use
useBattery — tracks device battery state. useGeolocation — tracks geo location state of user's device. useHover and useHoverDirty — tracks mouse hover state of some element. useHash — tracks location hash value. useIdle — tracks whether user is being inactive.useIntersection — tracks an HTML element's intersection. useKey, useKeyPress, useKeyboardJs, and useKeyPressEvent — track keys. useLocation and useSearchParam — tracks page navigation bar location state.useLongPress — tracks long press gesture of some element.useMedia — tracks state of a CSS media query. useMediaDevices — tracks state of connected hardware devices.useMotion — tracks state of device's motion sensor.useMouse and useMouseHovered — tracks state of mouse position. useMouseWheel — tracks deltaY of scrolled mouse wheel. useNetworkState — tracks the state of browser's network connection. useOrientation — tracks state of device's screen orientation.usePageLeave — triggers when mouse leaves page boundaries.useScratch — tracks mouse click-and-scrub state.useScroll — tracks an HTML element's scroll position. useScrolling — tracks whether HTML element is scrolling.useStartTyping — detects when user starts typing.useWindowScroll — tracks Window scroll position. useWindowSize — tracks Window dimensions. useMeasure and useSize — tracks an HTML element's dimensions. createBreakpoint — tracks innerWidthuseScrollbarWidth — detects browser's native scrollbars width. usePinchZoom — tracks pointer events to detect pinch zoom in and out status. useAudio — plays audio and exposes its controls. useClickAway — triggers callback when user clicks outside target area.useCss — dynamically adjusts CSS.useDrop and useDropArea — tracks file, link and copy-paste drops.useFullscreen — display an element or video full-screen. useSlider — provides slide behavior over any HTML element. useSpeech — synthesizes speech from a text string. useVibrate — provide physical feedback using the Vibration API. useVideo — plays video, tracks its state, and exposes playback controls. useRaf — re-renders component on each requestAnimationFrame.useInterval and useHarmonicIntervalFn — re-renders component on a set interval using setInterval.useSpring — interpolates number over time according to spring dynamics.useTimeout — re-renders component after a timeout.useTimeoutFn — calls given function after a timeout. useTween — re-renders component, while tweening a number from 0 to 1. useUpdate — returns a callback, which re-renders component when called.
useAsync, useAsyncFn, and useAsyncRetry — resolves an async function.useBeforeUnload — shows browser alert when user try to reload or close the page.useCookie — provides way to read, update and delete a cookie. useCopyToClipboard — copies text to clipboard.useDebounce — debounces a function. useError — error dispatcher. useFavicon — sets favicon of the page.useLocalStorage — manages a value in localStorage.useLockBodyScroll — lock scrolling of the body element.useRafLoop — calls given function inside the RAF loop.useSessionStorage — manages a value in sessionStorage.useThrottle and useThrottleFn — throttles a function. useTitle — sets title of the page.usePermission — query permission status for browser APIs.
useEffectOnce — a modified useEffect hook that only runs once.useEvent — subscribe to events.useLifecycles — calls mount and unmount callbacks.useMountedState and useUnmountPromise — track if component is mounted.usePromise — resolves promise only while component is mounted.useLogger — logs in console as component goes through life-cycles.useMount — calls mount callbacks.useUnmount — calls unmount callbacks.useUpdateEffect — run an effect only on updates.useIsomorphicLayoutEffect — useLayoutEffect that that works on server.useDeepCompareEffect, useShallowCompareEffect, and useCustomCompareEffect
createMemo — factory of memoized hooks.createReducer — factory of reducer hooks with custom middleware.createReducerContext and createStateContext — factory of hooks for a sharing state between components.useDefault — returns the default value when state is null or undefined.useGetSet — returns state getter get() instead of raw state.useGetSetState — as if useGetSet and useSetState had a baby.useLatest — returns the latest state or propsusePrevious — returns the previous state or props. usePreviousDistinct — like usePrevious but with a predicate to determine if previous should update.useObservable — tracks latest value of an Observable.useRafState — creates setState method which only updates after requestAnimationFrame. useSetState — creates setState method which works like this.setState. useStateList — circularly iterates over an array. useToggle and useBoolean — tracks state of a boolean. useCounter and useNumber — tracks state of a number. useList useUpsertuseMap — tracks state of an object. useSet — tracks state of a Set. useQueue — implements simple queue.useStateValidator — tracks state of an object. useStateWithHistory — stores previous state values and provides handles to travel through them. useMultiStateValidator — alike the useStateValidator, but tracks multiple states at a time. useMediatedState — like the regular useState but with mediation by custom function. useFirstMountState — check if current render is first. useRendersCount — count component renders. createGlobalState — cross component shared state.useMethods — neat alternative to useReducer. useEnsuredForwardedRef and ensuredForwardRef — use a React.forwardedRef safely.
Usage — how to import.
Unlicense — public domain.
Support — add yourself to backer list below.