Best React Hook Libraries You Should Know in 2026
·PkgPulse Team
TL;DR
usehooks-ts for TypeScript-first hooks; react-use for the complete collection; ahooks for enterprise React apps. usehooks-ts (~3M weekly downloads) is the TypeScript-native collection — smaller, well-typed, tree-shakeable. react-use (~8M downloads) is the massive collection (100+ hooks) but has a mix of quality. ahooks (~2M) from Alibaba is comprehensive and well-maintained for enterprise use cases. For most projects, usehooks-ts covers the essentials with the best TypeScript support.
Key Takeaways
- react-use: ~8M weekly downloads — 100+ hooks, comprehensive, some outdated
- usehooks-ts: ~3M downloads — TypeScript-first, tree-shakeable, actively maintained
- ahooks: ~2M downloads — Alibaba, enterprise patterns, request management
- TanStack Query — best async/data fetching hook (not just a utility library)
- 2026 trend — write custom hooks over installing large libraries for simple use cases
usehooks-ts (TypeScript-First)
// usehooks-ts — essential hooks with great TypeScript support
import {
useLocalStorage,
useDebounce,
useEventListener,
useOnClickOutside,
useWindowSize,
useMediaQuery,
useToggle,
useCounter,
useCopyToClipboard,
useIntersectionObserver,
useFetch,
} from 'usehooks-ts';
// useLocalStorage — persisted state with TypeScript type inference
import { useLocalStorage } from 'usehooks-ts';
interface UserPreferences {
theme: 'light' | 'dark';
language: string;
notificationsEnabled: boolean;
}
function Settings() {
const [preferences, setPreferences] = useLocalStorage<UserPreferences>(
'user-preferences',
{ theme: 'light', language: 'en', notificationsEnabled: true }
);
// Fully typed — TypeScript knows the shape
return (
<div>
<select
value={preferences.theme}
onChange={(e) => setPreferences(prev => ({
...prev,
theme: e.target.value as 'light' | 'dark',
}))}
>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</div>
);
}
// useDebounce — debounce search input
import { useDebounce } from 'usehooks-ts';
import { useEffect, useState } from 'react';
function SearchBar({ onSearch }) {
const [inputValue, setInputValue] = useState('');
const debouncedValue = useDebounce(inputValue, 300); // 300ms delay
useEffect(() => {
if (debouncedValue) {
onSearch(debouncedValue); // Only fires after typing stops
}
}, [debouncedValue]);
return (
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="Search packages..."
/>
);
}
// useOnClickOutside — close dropdowns
import { useRef } from 'react';
import { useOnClickOutside } from 'usehooks-ts';
function Dropdown() {
const [isOpen, setIsOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null);
useOnClickOutside(ref, () => setIsOpen(false));
return (
<div ref={ref}>
<button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
{isOpen && <div className="dropdown-menu">Menu content</div>}
</div>
);
}
// useIntersectionObserver — lazy loading, infinite scroll
import { useIntersectionObserver } from 'usehooks-ts';
import { useRef } from 'react';
function InfiniteList({ onLoadMore }) {
const sentinelRef = useRef<HTMLDivElement>(null);
const entry = useIntersectionObserver(sentinelRef, {
threshold: 0,
rootMargin: '100px', // Trigger 100px before visible
});
useEffect(() => {
if (entry?.isIntersecting) {
onLoadMore();
}
}, [entry?.isIntersecting]);
return (
<div>
{/* List items */}
<div ref={sentinelRef} /> {/* Invisible sentinel */}
</div>
);
}
react-use (Comprehensive)
// react-use — highlights from 100+ hooks
import {
useAsync, // Async state management
usePrevious, // Previous value tracker
useTitle, // Document title
useFavicon, // Favicon changer
useFullscreen, // Fullscreen API
useLongPress, // Long press detection
useMouseHovered, // Mouse position tracking
useIdle, // User idle detection
useSpeech, // Web Speech API
useGeolocation, // Geolocation
} from 'react-use';
// usePrevious — track previous state value
import { usePrevious } from 'react-use';
function AnimatedCounter({ count }) {
const prevCount = usePrevious(count);
return (
<div className={count > prevCount ? 'count-up' : 'count-down'}>
{count}
</div>
);
}
// useIdle — detect user inactivity (session timeout)
import { useIdle } from 'react-use';
function SessionManager() {
const isIdle = useIdle(300000); // 5 minutes
useEffect(() => {
if (isIdle) {
showSessionTimeoutWarning();
}
}, [isIdle]);
return null;
}
ahooks (Enterprise)
// ahooks — enterprise-grade patterns
import {
useRequest, // Data fetching with loading/error
useAntdTable, // Ant Design table integration
useVirtualList, // Virtual scrolling
useWebSocket, // WebSocket management
useCountDown, // Countdown timer
useRafState, // requestAnimationFrame state
useDebounceFn, // Debounced function
useThrottleFn, // Throttled function
useLatest, // Latest value ref
useLockFn, // Prevent concurrent executions
} from 'ahooks';
// ahooks useRequest — data fetching with auto-handling
import { useRequest } from 'ahooks';
async function fetchUser(id: number) {
const res = await fetch(`/api/users/${id}`);
return res.json();
}
function UserProfile({ userId }) {
const { data, loading, error, refresh } = useRequest(
() => fetchUser(userId),
{
// Re-fetch when userId changes
refreshDeps: [userId],
// Polling
pollingInterval: 30000, // Refresh every 30 seconds
// Caching
cacheKey: `user-${userId}`,
staleTime: 60000, // Cache valid for 60 seconds
}
);
if (loading) return <Spinner />;
if (error) return <button onClick={refresh}>Retry</button>;
return <div>{data?.name}</div>;
}
When to Choose
| Use Case | Hook | Library |
|---|---|---|
| Local storage with typing | useLocalStorage | usehooks-ts |
| Debounced search | useDebounce | usehooks-ts |
| Click outside | useOnClickOutside | usehooks-ts |
| Infinite scroll | useIntersectionObserver | usehooks-ts |
| Media query | useMediaQuery | usehooks-ts |
| Previous value | usePrevious | react-use |
| User idle detection | useIdle | react-use |
| Data fetching + cache | useRequest | ahooks |
| Virtual list | useVirtualList | ahooks |
| Any async state | useQuery | TanStack Query |
Compare React hook library package health on PkgPulse.
See the live comparison
View react use vs. usehooks on PkgPulse →