DevAcademy
LearnReactCustom Hooks
IntermediateReact

Custom Hooks

Learn how to extract and reuse stateful logic across components by writing your own custom hooks.

Reading Time

16 min

Lesson

Lesson 18 of 42

What is a Custom Hook?

A custom hook is simply a JavaScript function whose name starts with "use" and that calls other hooks inside it. It lets you extract stateful logic out of a component so it can be reused across multiple components.

The Problem: Duplicated Stateful Logic

When the same pattern of useState and useEffect calls shows up in several components (like tracking window size, or fetching data), copy-pasting that logic everywhere becomes hard to maintain.

A Custom Hook: useWindowWidth

import { useState, useEffect } from "react";

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    function handleResize() {
      setWidth(window.innerWidth);
    }

    window.addEventListener("resize", handleResize);
    return () => window.removeEventListener("resize", handleResize);
  }, []);

  return width;
}

Using the Custom Hook

function ResponsiveBanner() {
  const width = useWindowWidth();

  return <p>{width < 600 ? "Mobile view" : "Desktop view"}</p>;
}

function Sidebar() {
  const width = useWindowWidth(); // reused, with its own independent state
  return <aside style={{ display: width < 600 ? "none" : "block" }}>Sidebar</aside>;
}

Each Call Gets Its Own State

Every component that calls a custom hook gets a completely independent copy of its internal state — calling useWindowWidth() in two components doesn’t share state between them, it just shares the logic.

A Custom Hook for Fetching Data

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    setLoading(true);
    fetch(url)
      .then((res) => res.json())
      .then((json) => {
        setData(json);
        setLoading(false);
      });
  }, [url]);

  return { data, loading };
}

function UserProfile({ userId }) {
  const { data: user, loading } = useFetch(`/api/users/${userId}`);

  if (loading) return <p>Loading...</p>;
  return <h2>{user.name}</h2>;
}

Rules for Custom Hooks

RuleWhy
Name must start with "use"Lets React (and linters) know it follows the Rules of Hooks
Only call hooks at the top levelNot inside loops, conditions, or nested functions
Only call hooks from React functionsComponents or other custom hooks — never plain JavaScript functions

Custom Hooks Are About Reusing Logic, Not UI

A custom hook shares behavior (stateful logic and side effects) between components, not JSX markup. To reuse JSX, write a regular component instead — the two techniques solve different problems and are often combined.

Best Practice

Extract a custom hook as soon as you copy-paste the same useState/useEffect pattern into a second component — it’s one of the most effective ways to keep React components focused and DRY.

Interview Questions

Quick Quiz

1. What naming convention must a custom hook follow?

2. Do two components calling the same custom hook share the same state?

3. What does a custom hook primarily let you reuse?