← Back to React Course | Chapter 6: Custom Hooks & Advanced Patterns | Lesson 1 of 10

Building Your First Custom Hook

A custom hook is like writing your own recipe once so you can reuse it in any dish instead of writing the same steps over and over.

What Problem Custom Hooks Solve

When two different components need the same piece of stateful logic, like tracking window size or a counter, copying that logic into both components duplicates code and makes updates error-prone. A custom hook lets you write that logic once as a reusable function. Any component can then call it to get the same behavior.

Note: If you find yourself copying a useState+useEffect pair into a second component, that's usually a sign it should become a custom hook.

Warning: A custom hook shares its LOGIC, not its STATE — two components calling the same custom hook still get independent, separate state.

Example: What Problem Custom Hooks Solve

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
function useCounter(start) {
  const [count, setCount] = React.useState(start);
  const increment = () => setCount(c => c + 1);
  return { count, increment };
}
function App() {
  const { count, increment } = useCounter(0);
  return <button onClick={increment}>Count: {count}</button>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
  </script>
</body>
</html>

The 'use' Naming Convention

A custom hook's name must start with lowercase use, like useCounter or useFetch. This isn't just a style choice — React's linter and the rules-of-hooks system use this prefix to know a function is allowed to call other hooks inside it. A regular helper function without the use prefix cannot call useState or useEffect safely.

Note: Name your hook after what it returns or does, like useWindowWidth or useOnlineStatus, so it's self-documenting.

Warning: Naming a function useHelper() when it doesn't actually call any hooks is misleading — only name it 'use...' if it genuinely uses hooks internally.

Example: The 'use' Naming Convention

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
function useToggleValue(initial) {
  const [value, setValue] = React.useState(initial);
  const toggle = () => setValue(v => !v);
  return [value, toggle];
}
function App() {
  const [isOn, toggle] = useToggleValue(false);
  return <button onClick={toggle}>{isOn ? "ON" : "OFF"}</button>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
  </script>
</body>
</html>

Building Your First Custom Hook

To build a custom hook, write a normal JavaScript function, call one or more built-in hooks inside it, and return whatever data or functions the calling component needs. This example builds useWindowWidth, which tracks the browser window's width and updates automatically on resize.

Note: Return an array (like useState does) when order matters, or an object when you want named, self-documenting values.

Warning: Always clean up any event listeners you add inside a custom hook's useEffect, or they'll pile up every time the component re-renders.

Example: Building Your First Custom Hook

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
function useWindowWidth() {
  const [width, setWidth] = React.useState(window.innerWidth);
  React.useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener("resize", handleResize);
    return () => window.removeEventListener("resize", handleResize);
  }, []);
  return width;
}
function App() {
  const width = useWindowWidth();
  return <p>Window width: {width}px</p>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
  </script>
</body>
</html>
Common Mistakes
  1. Forgetting to prefix the function name with use, which breaks React's rules-of-hooks linting and hook detection.
  2. Copy-pasting the same useState+useEffect logic into multiple components instead of extracting it into a custom hook.
  3. Calling a custom hook conditionally (inside an if-statement), which violates the same rules that apply to built-in hooks.
Chapter Summary
  • A custom hook is a JavaScript function whose name starts with use and that calls other hooks inside it.
  • Custom hooks let you extract and reuse stateful logic across multiple components.
  • They follow the same rules of hooks as built-in hooks (top-level calls only, no conditionals).
  • Each component that uses a custom hook gets its own independent state, not a shared one.
Browser Support

Available since React 16.8 (Hooks introduction) — custom hooks are just plain functions built on top of built-in hooks.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.