A custom hook in React is a JavaScript function that leverages React’s built-in hooks—like useState
, useEffect
, useReducer
, etc.—to encapsulate and reuse logic across multiple components. Without custom hooks, you’re just asking for messy, duplicated code everywhere. By extracting shared logic into reusable functions, you can make your components cleaner, more organized, and laser-focused on what they’re actually supposed to do. If you’re not using custom hooks, you’re making your life harder than it needs to be.
If you’re creating a custom hook, it must start with “use”—no exceptions. This isn’t just a suggestion, it’s a rule. React uses this naming convention to treat your function like a hook, ensuring it follows the same strict rules as built-in hooks. That means it gets called in the same order every time the component renders. Ignore this, and you’ll end up with chaos. Stick to the rules, and React will do its job properly.
Why You Should Use a Custom Hook
- Code Reusability: Custom hooks allow you to abstract away logic that can be reused in multiple components, reducing duplication and making your code DRY (Don’t Repeat Yourself).
- Separation of Concerns: By creating custom hooks, you can separate complex logic from the component UI, making the component itself simpler and more focused on rendering.
- Maintainability: Custom hooks make it easier to maintain your codebase. If you need to fix or update a piece of logic, you only need to modify the custom hook, and all components that use it will automatically get the updated behavior.
- Testing: Custom hooks can be easily tested independently from the UI components, making your logic more modular and testable.
- Composability: Custom hooks can be composed together. You can create smaller, more focused hooks and combine them to create complex functionality.

When You Should Use a Custom Hook
Knowing how to use a custom hook doesn’t mean you should be using one. Don’t fall into the trap of overusing them. You should only create a custom hook when it’s absolutely necessary—when you find yourself facing these scenarios:
- Shared Logic: When you find yourself repeating the same logic in multiple components, such as handling form inputs, fetching data from an API, or managing timers.
- Complex Logic: If you have a complex or stateful logic that doesn’t belong in the component itself but still needs to be kept separate for clarity or maintainability.
- Refactoring: When your components are becoming too large and complex, extracting reusable logic into custom hooks can help keep your components clean and readable.
How to Use a Custom Hook
Let’s see examples of custom hooks that you can build:
1. A useLocalStorage
Hook
Let’s create a custom hook that interacts with the browser’s localStorage
. This hook will allow us to read and write data from localStorage
easily.
import { useState } from 'react';
// Custom Hook to interact with localStorage
function useLocalStorage(key, initialValue) {
// Get stored value or fallback to initial value
const storedValue = localStorage.getItem(key);
// State to hold the value
const [value, setValue] = useState(storedValue ? JSON.parse(storedValue) : initialValue);
// Update localStorage and state
const setStoredValue = (newValue) => {
setValue(newValue);
localStorage.setItem(key, JSON.stringify(newValue));
};
return [value, setStoredValue];
}
export default useLocalStorage;
Here’s a breakdown of how this works:
- State Management:
useState
is used to hold the state of the value, either fromlocalStorage
or the initial value passed to the hook. - Side Effect: When the
setStoredValue
function is called, it updates both the React state and thelocalStorage
at the same time, keeping them in sync. - Return: The hook returns the value from the state and a function (
setStoredValue
) that allows components to update that value.
Now, you can use this custom hook in your components like this:
import React from 'react';
import useLocalStorage from './useLocalStorage';
function App() {
const [name, setName] = useLocalStorage('name', 'John Doe');
return (
<div>
<h1>Hello, {name}!</h1>
<button onClick={() => setName('Jane Doe')}>Change Name</button>
</div>
);
}
export default App;
2. A useFetch
Hook for Data Fetching
Here’s another example—a useFetch
hook that abstracts the logic for fetching data from an API.
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Failed to fetch');
}
const result = await response.json();
setData(result);
} catch (error) {
setError(error.message);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetch;
You can use this useFetch
hook in your components to simplify data fetching:
import React from 'react';
import useFetch from './useFetch';
function App() {
const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/posts');
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div>
<h1>Fetched Posts</h1>
<ul>
{data.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
export default App;
Best Practices for Custom Hooks
- Keep Hooks Focused: A custom hook should ideally do one thing well. If you find yourself trying to do too much in one hook, consider breaking it down into smaller, more manageable hooks.
- Reuse Built-in Hooks: Custom hooks are typically built using existing React hooks, so take advantage of them. Don’t reinvent the wheel; use
useState
,useEffect
,useContext
, etc., to manage state and side effects. - Naming Convention: Always name your custom hooks starting with the word
use
to follow React’s conventions. This helps with readability and makes it clear that the function is a hook. - Don’t Call Hooks Conditionally: Like React’s built-in hooks, custom hooks must follow the rules of hooks—don’t call them conditionally or inside loops.
Conclusion
Custom hooks are a powerful feature of React that can make your components cleaner, more reusable, and easier to maintain. By encapsulating logic into custom hooks, you can improve the modularity of your code, reduce duplication, and keep your components focused on rendering UI. Whether you’re dealing with complex state, side effects, or external APIs, custom hooks give you the flexibility to build more scalable and maintainable React applications.