React  

Creating Your First Custom Hook in React

If you've ever repeated the same useEffect, useState, or API-fetching logic across React components, you're not alone. That’s exactly where custom hooks come in; they allow you to encapsulate shared logic in a reusable way.

In this article, you’ll not only learn how to build your first custom hook from scratch, but also why custom hooks exist, when to use them, and how to avoid common mistakes. Whether you're a React beginner or looking to level up your skills, this guide is for you.

What Are Custom Hooks in React?

In React, custom hooks are JavaScript functions that start with use and can use other hooks internally. Think of them as a way to abstract complex logic from your components into small, clean, reusable pieces of code.

Let’s say multiple components in your app need to:

  • Fetch data from an API
  • Manage a form’s state
  • Track mouse position or window size
  • Store values in localStorage

Instead of writing the same useEffect, useState, or addEventListener logic in each component, you can wrap that logic into a custom hook, like:

const { data, loading, error } = useFetch('/api/products');

Simple, clean, readable.

Why Use a Custom Hook?

React is great for building reusable UI components, but it also encourages the reuse of logic through hooks.

Here’s what custom hooks give you:

Benefit Explanation
DRY Code Avoid repeating the same state/effect logic
Reusability Use across multiple components
Clean Components Keep components focused on rendering
Testability Hooks can be unit tested in isolation
Modularity Separate side effects, fetching, or listeners from UI

Custom hooks let you write business logic once and reuse it everywhere, just like components let you reuse UI.

Quick Recap: Rules of Hooks

Before writing your own, remember React's golden rules of hooks:

  1. Hooks must start with use: to enable linting and hook behavior.
  2. Call hooks at the top level: never inside loops, conditions, or nested functions.
  3. Call hooks only inside React functions: either a component or another hook.

Let's Build One: useFetch Custom Hook

Let’s say you want to fetch data from an API like JSONPlaceholder. You need:

  • A loading state
  • A data response
  • An error handler

Normally, you'd write this in each component. But now, let’s wrap it into a hook.

useFetch.js

import { useState, useEffect } from 'react';

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

  useEffect(() => {
    let isMounted = true;

    async function fetchData() {
      try {
        const response = await fetch(url);
        if (!response.ok) throw new Error('Network response was not ok');
        const json = await response.json();

        if (isMounted) {
          setData(json);
          setLoading(false);
        }
      } catch (err) {
        if (isMounted) {
          setError(err);
          setLoading(false);
        }
      }
    }

    fetchData();

    return () => {
      isMounted = false;
    };
  }, [url]);

  return { data, loading, error };
}

export default useFetch;

Using the Custom Hook in a Component

App.js

import React from 'react';
import useFetch from './useFetch';

function App() {
  const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/posts/1');

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <h2>{data.title}</h2>
      <p>{data.body}</p>
    </div>
  );
}

export default App;

That’s it! You just reused data-fetching logic across multiple components in a clean, reusable way.

Best Practices for Writing Custom Hooks

Here are some battle-tested tips to ensure your hooks are useful and maintainable:

  1. Name them with use prefix: useAuth, useForm, etc.
  2. Keep them focused: Don’t mix unrelated logic in one hook.
  3. Document return values: Make it easy for teammates to use.
  4. Parameterize them: Accept arguments like url, config, etc.
  5. Test them in isolation: With @testing-library/react-hooks or similar tools.

Real-World Custom Hook Ideas

Here are some practical hooks you can build next:

Hook Name What It Does
useLocalStorage(key, defaultValue) Sync state with localStorage
useWindowSize() Track current window size (responsive design helper)
useDebounce(value, delay) Debounce any input or value
usePrevious(value) Track previous value of state or prop
useClickOutside(ref, callback) Detect clicks outside an element

Final Thoughts

Custom hooks are one of React’s best-kept secrets for clean, scalable frontend development.

By extracting logic into reusable functions, you avoid bloat, increase testability, and make your components laser-focused on rendering UI.

The more you use hooks, the more you’ll appreciate how React encourages not just reusable UI, but reusable behavior.

What’s Next?

If you found this useful, here are some ways to go deeper:

  • Build a useForm hook to manage form state + validation
  • Combine multiple hooks to create more powerful abstractions
  • Try integrating a custom hook with Zustand, Axios, or Firebase