Course topics

By WebNest Studio Engineering Team

React Tutorial

Building Custom Hooks

A custom hook is simply a JavaScript function, named starting with "use", that calls one or more of React's built-in hooks internally to package up reusable stateful logic. Custom hooks let you extract logic like form handling, data fetching, or toggling a boolean flag out of a component and share it across many components, without duplicating the same useState/useEffect code everywhere.

Because a custom hook is "just a function," it follows exactly the same Rules of Hooks as any built-in hook: it must be called at the top level of a component or another custom hook, and its own internal hook calls must happen in the same order every time it runs.

Why it matters

When two components need the same stateful logic, such as toggling a flag or loading data, copying it into both doubles the bugs. A custom hook packages that logic in a function that any component can call. It is the main mechanism for reuse in modern React, and most libraries expose their features as hooks.

Why Extract a Custom Hook

If you notice the same combination of useState and useEffect logic appearing in multiple components — such as tracking whether a value is toggled, tracking the browser window's size, or fetching data from an endpoint — that is a strong signal to extract the shared logic into a custom hook. The component that uses the hook stays focused on rendering, while the hook owns the "how" of managing that piece of behavior.

A useToggle Example

A very common, minimal custom hook is one that manages a boolean flag with convenient toggle behavior, useful for things like modals, accordions, and dropdown menus. Internally it is nothing more than a useState call wrapped in a small, reusable API.

A useFetch Example

A more involved custom hook can wrap data fetching, tracking loading and error states alongside the data itself, and re-fetching whenever its input (such as a URL) changes — using useEffect and useState together and returning a clean object of values for the consuming component to read.

  • Custom hooks return whatever plain values, objects, or arrays make sense for their use case — there is no single required shape.
  • A custom hook can call other custom hooks, letting you compose small hooks into larger ones.
  • Naming a function "useSomething" is a convention that also enables the Rules of Hooks ESLint checks to correctly recognize it as a hook.

Example

Two custom hooks: useToggle for a boolean flag, and useFetch for data loading

JavaScript
import { useState, useEffect } from 'react';

// A small reusable hook for toggling a boolean value.
function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue);
  const toggle = () => setValue((prev) => !prev);
  return [value, toggle];
}

// A reusable hook for fetching JSON data from a URL.
function useFetch(url) {
  const [data, setData] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    let isCancelled = false;
    setIsLoading(true);

    fetch(url)
      .then((res) => res.json())
      .then((json) => { if (!isCancelled) setData(json); })
      .catch((err) => { if (!isCancelled) setError(err); })
      .finally(() => { if (!isCancelled) setIsLoading(false); });

    return () => { isCancelled = true; };
  }, [url]);

  return { data, isLoading, error };
}

function ProfilePanel({ userId }) {
  const [isOpen, toggleOpen] = useToggle(false);
  const { data: user, isLoading } = useFetch(`/api/users/${userId}`);

  return (
    <div>
      <button onClick={toggleOpen}>{isOpen ? 'Hide' : 'Show'} profile</button>
      {isOpen && (isLoading ? <p>Loading...</p> : <p>{user?.name}</p>)}
    </div>
  );
}

export default ProfilePanel;
Output
The button toggles between "Show profile" and "Hide profile" on each click, using the reusable useToggle hook; when shown, it briefly displays "Loading..." and then the fetched user's name, all powered by the reusable useFetch hook without duplicating that fetching logic inside ProfilePanel itself.

Common Mistakes

  • Naming a reusable function without the "use" prefix, which prevents React and ESLint from recognizing it as a hook and enforcing the Rules of Hooks on it.
  • Duplicating the same useState/useEffect pattern across many components instead of noticing the repetition and extracting a custom hook.
  • Breaking the Rules of Hooks inside a custom hook itself, such as calling useState conditionally within it — a custom hook is not exempt from the same rules.
  • Making a custom hook do too many unrelated things at once instead of keeping it focused on one specific piece of reusable logic.

Exercise

Do this in src/App.jsx of a Vite + React project. Write a custom hook useToggle that takes an initial boolean and returns the current value and a function that flips it. Use it in App to show and hide a paragraph with a button.

Expected result: the button reads Show; clicking it reveals the paragraph and changes the label to Hide, and clicking again hides it.

JavaScript · starter code
import { useState } from "react";

// TODO: function useToggle(initial = false) returning [value, toggle]

export default function App() {
  // TODO: use the hook

  return (
    <div>
      {/* TODO: a button labelled "Hide" or "Show" that toggles */}
      {/* TODO: the paragraph "Now you see me", only when open */}
    </div>
  );
}
Show hints
  • A custom hook is a function whose name starts with use and which calls other hooks.
  • Return the pair as an array, so the caller can name the two values as it likes.
Show solution
JavaScript · solution
import { useState } from "react";

function useToggle(initial = false) {
  const [value, setValue] = useState(initial);
  const toggle = () => setValue((current) => !current);
  return [value, toggle];
}

export default function App() {
  const [open, toggleOpen] = useToggle(false);

  return (
    <div>
      <button onClick={toggleOpen}>{open ? "Hide" : "Show"}</button>
      {open && <p>Now you see me</p>}
    </div>
  );
}

Quick quiz

1. How must the name of a custom hook begin?

  • A. With hook
  • B. With a capital letter
  • C. With use
  • D. With an underscore
Show answer

C. The prefix lets React and the lint rules recognise it as a hook.

2. Two components call the same custom hook. Do they share its state?

  • A. No; each call has its own independent state
  • B. Yes, always
  • C. Only if they are siblings
  • D. Only in production
Show answer

A. A custom hook shares logic, not data.

3. What can a custom hook do that an ordinary helper function cannot?

  • A. Return a value
  • B. Take arguments
  • C. Run a loop
  • D. Call other hooks such as useState and useEffect
Show answer

D. That is the only real difference, and the reason for the naming rule.

Interview questions

What is a custom hook and why would you write one?

Show answer

A custom hook is a JavaScript function whose name starts with use and which calls other hooks. It extracts stateful logic, such as a toggle, a form field, a data request or a subscription to window size, out of a component so that it can be reused and tested separately. Each component that calls it gets its own state. It keeps components focused on what they render.

How do custom hooks compare with higher-order components and render props?

Show answer

All three share logic between components. A higher-order component wraps a component in another, and render props pass a function as a child; both add layers to the component tree, which makes it deep and harder to debug, and the origin of each prop is unclear. A custom hook adds no component at all: the logic is called directly inside the component, and its inputs and outputs are explicit.

Key Points to Remember

  • A custom hook is a function starting with "use" that calls other hooks internally to package up reusable logic.
  • Custom hooks let you share stateful logic (toggling, fetching, subscriptions) across components without duplicating code.
  • Custom hooks must follow the same Rules of Hooks as built-in hooks, including consistent call order.
  • Custom hooks can be composed from other custom hooks, and return whatever shape of data best fits their use case.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Shows a live preview of plain HTML, CSS and JavaScript. JSX needs a React project on your computer. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

These examples need a project environment on your computer, so they can be copied but not run here. Codelab runs Python, FastAPI backends with SQLite, plain Java 17 and HTML/CSS/JavaScript.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering