React Custom Hooks

React custom hooks let you extract stateful logic from components into reusable functions. A custom hook is simply a function whose name starts with use and that calls other hooks. Each component that uses it gets its own independent state.

A Simple useToggle Hook

import { useState, useCallback } from 'react';

export function useToggle(initial = false) {
  const [on, setOn] = useState(initial);
  const toggle = useCallback(() => setOn((v) => !v), []);
  return [on, toggle];
}

function Faq() {
  const [open, toggle] = useToggle();
  return (
    <>
      <button onClick={toggle}>{open ? 'Hide' : 'Show'} answer</button>
      {open && <p>Hooks start with "use".</p>}
    </>
  );
}
Show answer
(click)
Hide answer
Hooks start with "use".

A useFetch Hook

Data fetching code repeats in every component. Moving it into a hook keeps components focused on display.

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

  useEffect(() => {
    const controller = new AbortController();
    setLoading(true);
    fetch(url, { signal: controller.signal })
      .then((r) => (r.ok ? r.json() : Promise.reject(new Error(r.statusText))))
      .then(setData)
      .catch((e) => e.name !== 'AbortError' && setError(e))
      .finally(() => setLoading(false));
    return () => controller.abort();
  }, [url]);

  return { data, error, loading };
}

function Repo() {
  const { data, loading } = useFetch('https://api.github.com/repos/facebook/react');
  return loading ? <p>Loading...</p> : <p>React has {data.stargazers_count} stars</p>;
}
Loading...
React has 230000 stars

A useLocalStorage Hook

Passing a function to useState is lazy initialization: it runs only on the first render, so we do not read localStorage every time.

export function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    const saved = localStorage.getItem(key);
    return saved !== null ? JSON.parse(saved) : initialValue;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue];
}

// const [theme, setTheme] = useLocalStorage('theme', 'light');
(theme survives page reloads)

Rules for Custom Hooks

  • The name must start with use so React and linters can check the rules of hooks.
  • Call hooks only at the top level of your hook, never in loops or conditions.
  • Hooks share logic, not state: two components using useToggle each get their own value.
  • Return whatever is most convenient: a value, an array like useState, or an object.

Frequently asked questions

What is a custom hook in React?

A JavaScript function whose name starts with use and that calls built-in hooks like useState or useEffect, letting you reuse stateful logic across components.

Do custom hooks share state between components?

No. Each call creates its own state. To share the same state, lift it up or put it in Context, possibly accessed through a custom hook.

Why must custom hooks start with "use"?

The prefix tells React tooling that the function follows the rules of hooks, so the linter can check that it is only called at the top level of components and other hooks.