Custom hooks are JavaScript functions that call other hooks. They let you extract component logic into reusable functions — useFetch, useDebounce, useLocalStorage — without changing the component tree.
Extracting Logic into Hooks
If two components share the same state + effect pattern, move it into a function named useSomething. Custom hooks can call useState, useEffect, and other hooks just like components.
Each call to a custom hook gets its own isolated state. Custom hooks share logic, not state — two components calling useCounter() each have separate counts.
Debounce hook delays updating a value until typing pauses — ideal for search inputs.
Custom Hook Conventions
// Must start with "use"
function useOnlineStatus() { ... }
// Return whatever API makes sense
return value;
return [value, setValue];
return { data, loading, error };
Name must start with use so ESLint can enforce hook rules.
Only call custom hooks from components or other custom hooks.
Keep hooks focused — one responsibility per hook.
Colocate in src/hooks/ for discoverability.
Popular Custom Hook Patterns
Hooks you'll build or install frequently.
Hook
Purpose
useDebounce
Delay rapid value changes
useLocalStorage
Persist state in localStorage
useMediaQuery
Responsive breakpoint detection
useOnClickOutside
Close dropdown on outside click
useFetch
Simple data fetching (prefer TanStack Query)
useToggle
Boolean flip helper
Testing Custom Hooks
Use @testing-library/react's renderHook and act to test hooks in isolation without mounting a full component.