Custom Hooks

A custom hook is a JavaScript function whose name starts with use and that calls other hooks inside it. It lets you extract stateful logic from a component so you can reuse it anywhere — without repeating yourself. By the end of this lesson you'll write your own hooks, understand why each caller gets isolated state, and compose hooks together.

Learn Custom Hooks in our free React course — a beginner-friendly interactive lesson with runnable examples, a practice exercise and a quick recall.

Part of the free React course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

1️⃣ Anatomy of a Custom Hook

A custom hook is nothing magic — it's a function that uses other hooks and follows two rules: its name starts with use , and you only call it from a component or another hook. Here's the classic useCounter :

The demo below models the same idea with a factory function so you can watch two counters keep independent state:

2️⃣ Returning Values and Functions

A hook can return whatever shape is convenient. useState returns an array (so you can name the pair freely). Many hooks return an object (so the caller destructures by name). A common, tiny hook is useToggle :

Your turn. Fill in the blank so toggle() flips the boolean to its opposite:

3️⃣ Composing Hooks

The real power: hooks call other hooks. A useFetch hook bundles a request plus loading/error state, and a higher-level useUser builds on top of it — each layer stays small and testable.

The demo models that {' '} shape so you can see what such a hook returns for a hit and a miss:

These lines of a useLocalStorage hook are scrambled. Put them in the correct order:

1) Two components both call useCounter(0) . One increments to 5. What is the other's count?

Still 0. Each component gets its own isolated state — calling a hook shares logic, not data.

2) Why must a custom hook's name start with use ?

So React's linter can apply the Rules of Hooks (no calling hooks in loops/conditions). The prefix is how tooling knows a function contains hook calls.

3) Can you call useToggle() inside an if block?

No. Hooks (including custom ones) must run at the top level of a component or hook, in the same order every render.

📋 Quick Reference

Model a useToggle that starts true , then toggle it twice and log the result. The pattern is the engine behind every open/close panel in the world.

Practice quiz

What two things make a function a custom hook?

  • It returns JSX and takes props
  • It is async and uses fetch
  • Its name starts with 'use' and it calls other hooks
  • It is a class with a render method

Answer: Its name starts with 'use' and it calls other hooks. A custom hook's name starts with 'use' and it calls other hooks (useState, useEffect) internally.

Two components both call useCounter(0); one increments to 5. What is the other's count?

  • Still 0 — each call gets isolated state
  • 5
  • undefined
  • It throws an error

Answer: Still 0 — each call gets isolated state. Each component that calls a hook gets its own isolated state. Calling a hook shares logic, not data.

Why must a custom hook's name start with 'use'?

  • It is purely cosmetic
  • So it can be imported
  • So it runs faster
  • So React's linter can enforce the Rules of Hooks

Answer: So React's linter can enforce the Rules of Hooks. The 'use' prefix lets tooling know the function contains hook calls and apply the Rules of Hooks.

Can you call a custom hook inside an if block or loop?

  • Yes, anywhere
  • No — hooks must run at the top level, in the same order each render
  • Only inside loops
  • Only with useEffect

Answer: No — hooks must run at the top level, in the same order each render. Hooks (including custom ones) must be called at the top level of a component or hook, never conditionally.

Can a custom hook call another custom hook?

  • Yes — hooks compose freely
  • No, hooks cannot compose
  • Only class hooks can
  • Only inside useEffect

Answer: Yes — hooks compose freely. Hooks compose: a useUser() can call useFetch(), which itself calls useState and useEffect.

From where may you call a custom hook?

  • Anywhere, including module scope
  • Only inside event handlers
  • From a component or another hook, during render
  • Only inside setTimeout

Answer: From a component or another hook, during render. Hooks work during render, called from a component or another hook — not from plain handlers or module scope.

When is it a good time to extract a custom hook?

  • Never; keep all logic inline
  • When the same stateful logic is duplicated across components
  • Only for animations
  • Only when using TypeScript

Answer: When the same stateful logic is duplicated across components. Extract a hook when you find the same useState/useEffect logic repeated, or a component grows noisy.

What can a custom hook return?

  • Only a single value
  • Only JSX
  • Only a function
  • Whatever shape is convenient — a value, an array, or an object

Answer: Whatever shape is convenient — a value, an array, or an object. Hooks can return any shape: useState returns an array; many hooks return an object the caller destructures.

If two components must share the SAME data (not just logic), what should you do?

  • Call the same custom hook in both
  • Lift state up or use Context
  • Use two separate hooks
  • Nothing — hooks already share data

Answer: Lift state up or use Context. Custom hooks give each caller isolated state. To truly share data, lift state up or use Context.

Which return shape lets the caller freely name the pair, like useState does?

  • An object
  • A string
  • An array
  • A Map

Answer: An array. Returning an array (e.g. [on, toggle]) lets the caller name the pair anything via destructuring.