@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 @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.
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.

A collection of modern, server-safe React hooks – from the ui.dev team.
Compatible with React v18.0.0+.
npm i @uidotdev/usehooks
npm i @uidotdev/usehooks@experimental react@experimental react-dom@experimental