Custom Hooks
Master custom hooks — the most powerful pattern in React for sharing stateful logic between components. Learn how to extract, compose, and test custom hooks the way senior engineers do in enterprise applications.
Last reviewed: January 2025
🔍 What Is It?
A custom hook is a function that:
- Starts with
use— this naming convention lets React and ESLint enforce the Rules of Hooks - Calls other hooks — useState, useEffect, useCallback, or other custom hooks
- Returns values — state, derived data, handler functions, or any combination
- Has no JSX — custom hooks are pure logic, no rendering
Every time you call a custom hook in a component, it gets its own isolated state. Hooks share logic, not state.
🎯 Why Do We Need It?
Before custom hooks, sharing stateful logic between components required awkward patterns: Higher Order Components (HOC) or Render Props — both added wrapper components and made the component tree harder to read.
Custom hooks solve this cleanly:
- →No wrapper components — logic is extracted, not the component structure
- →Composable — hooks can call other hooks
- →Testable — test the hook in isolation with renderHook
- →Readable —
const { data, loading } = useUserProfile(userId)is self-documenting
🏗️ What Happens in Real Projects?
In a large enterprise app, custom hooks are everywhere:
- →
useAuth()— returns current user, isLoggedIn, login(), logout() - →
useApi<T>(url)— generic data fetching with loading/error/data states - →
useDebounce(value, delay)— debounces a rapidly-changing value (search input) - →
useLocalStorage(key, initial)— persisted state that survives page refresh - →
usePermissions()— returns what the current user can and cannot do - →
useForm(schema)— form state, validation, and submission handling - →
usePagination(total, pageSize)— page number, navigation functions
⚙️ How It Works
Custom hooks are regular JavaScript functions — React gives them no special treatment beyond the naming convention. When you call useAuth() in a component, React runs the function and all the hooks inside it are associated with that component instance.
Each call gets its own state:
If ComponentA and ComponentB both call useCounter(), they each get completely independent counter state. The hook shares logic, not state.
Composition:
Custom hooks can call other custom hooks. useUserProfile(id) might call useApi(url) internally. This creates a clean abstraction hierarchy.
💻 Example
Custom Hooks from Real Enterprise Projects — A generic API hook, a debounce hook, and a permissions hook.
🤔 Common Confusions
❓ Do custom hooks share state between components?
No. Each component that calls a custom hook gets its own isolated state. If ComponentA and ComponentB both call useCounter(), they have completely independent counters. To share state, the hook must read from a shared source: Context, Zustand store, or a module-level variable.
❓ Does a custom hook have to start with "use"?
Technically no, but you must name it starting with "use" for React's ESLint plugin to enforce the Rules of Hooks inside it. If you name it getUser() instead of useUser(), the linter won't warn you if you call it conditionally — which can cause subtle bugs. Always prefix with "use".
❓ Can a custom hook return JSX?
Technically yes, but it is a bad practice. Hooks should return data and functions, not UI. If you need to return UI, create a component. Mixing hooks and JSX blurs the line between logic and presentation, making code harder to test and maintain.
🚨 Common Production Issues
**Over-abstraction** — Extracting every 3-line useEffect into a custom hook creates an indirection maze. Extract when the pattern repeats 2+ times or when the hook provides a meaningful abstraction.
**Missing cleanup** — Custom hooks that set up subscriptions or timers must return cleanup via useEffect. Forgetting cleanup causes memory leaks in every component that uses the hook.
**Returning unstable references** — A hook that returns a new object or array on every call will cause re-renders in every consumer. Memoize return values with useMemo/useCallback when needed.
**Testing gaps** — Custom hooks that are not tested independently are hard to debug. Use @testing-library/react's renderHook to test hooks in isolation.
✅ Best Practices
Name hooks clearly: useUserProfile, not useData or useHelper
Return objects (not arrays) from hooks with multiple values — named properties are self-documenting
Memoize returned functions with useCallback to prevent re-renders in consumers
Test custom hooks with @testing-library/react renderHook
Co-locate hooks with the feature they serve — not in a global hooks/ folder
Document what the hook returns with TypeScript interfaces
Extract when a pattern repeats 2+ times, not preemptively
🏛️ Architect's Note
Custom hooks are the most powerful tool in the React architecture toolkit. They are how you build a domain-specific API on top of React primitives. In a well-architected app, components should read almost like plain English: const { user } = useAuth(), const { policies } = usePolicies(userId), const { can } = usePermissions(). The business logic lives in hooks; components are just the rendering layer. This separation makes components easy to read, hooks easy to test, and logic easy to reuse.
💬 Frequently Asked Questions
🎯 Key Takeaways
🔗 Related Topics
🎯 Practice this topic in interviews
↗ Where this topic fits
React Technology Guide
Return to the React learning hub.
Frontend Engineer Career Path
A beginner-friendly journey through web foundations, React, production frontend engineering, and interview preparation.
Python Developer Career Path
A practical route through Python fundamentals, automation, backend basics, testing, projects, interview preparation, and career growth.
Sources
Sources / References
Use these React references to verify the concept in official docs and continue into the most relevant SplashRide pages.
Official documentation
Related SplashRide pages
SplashRide React learning path
Browse the full React topic collection and learning order on SplashRide.
React Interview Prep
Practice React interview questions and scenario-based answers.
Frontend Engineer Career Path
A beginner-friendly journey through web foundations, React, production frontend engineering, and interview preparation.
Python Developer Career Path
A practical route through Python fundamentals, automation, backend basics, testing, projects, interview preparation, and career growth.
useState
Master useState — the most fundamental React hook. Learn how it works under the hood, the patterns senior engineers use, and the bugs that trip up even experienced developers.
useEffect
Master useEffect — the hook for synchronising React components with the outside world. Learn the dependency array, cleanup functions, and how to avoid the most common production bugs.