React Custom Hooks: Beginner’s Guide with Examples
In React, Hooks like useState, useEffect, useRef, and useMemo let us add state and side effects to functional components.
Sometimes, multiple components share the same logic — for example:
Fetching data from an API
Managing form inputs
Handling window resizing
Instead of duplicating code, we can create a Custom Hook.
A Custom Hook is a JavaScript function whose name starts with use and can call other hooks.
Why Use Custom Hooks?
Reusability: Share logic across multiple components
Clean code: Reduce duplication and make components simpler
Separation of concerns: Isolate logic from UI
Creating a Custom Hook
Example 1: useCounter Hook
import { useState } from "react";
// Custom Hook
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
const reset = () => setCount(initialValue);
return { count, increment, decrement, reset };
}
export default useCounter;
Using the Custom Hook
import React from "react";
import useCounter from "./useCounter";
function CounterComponent() {
const { count, increment, decrement, reset } = useCounter(5);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increase</button>
<button onClick={decrement}>Decrease</button>
<button onClick={reset}>Reset</button>
</div>
);
}
useCounter logic is reusable in any component.
Example 2: useFetch Hook
import { useState, useEffect } from "react";
// Custom Hook to fetch API data
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then((res) => res.json())
.then((data) => {
setData(data);
setLoading(false);
});
}, [url]);
return { data, loading };
}
export default useFetch;
Using useFetch
import React from "react";
import useFetch from "./useFetch";
function UsersList() {
const { data, loading } = useFetch("https://jsonplaceholder.typicode.com/users");
if (loading) return <p>Loading...</p>;
return (
<ul>
{data.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
The API fetching logic is separated and reusable.
Rules for Custom Hooks
Function name must start with
useCan call other hooks inside the custom hook
Cannot be called conditionally; always follow hook rules
Can return state, functions, objects, or arrays
Best Practices
| Practice | Explanation |
|---|---|
| Reusability | Create hooks for logic used across multiple components. |
| Separation of concerns | Keep UI components simple; logic goes in the custom hook. |
| Return consistent structure | Return an object or array for predictability. |
| Use descriptive names | useCounter, useFetch, useForm — names should describe the hook purpose. |
| Combine hooks | Custom hooks can use useState, useEffect, useRef, etc., internally. |
FAQ
Q1: Can custom hooks have state?
Yes, you can use useState or useReducer inside custom hooks.
Q2: Can I call multiple hooks inside a custom hook?
Yes, a custom hook can combine multiple built-in hooks for complex logic.
Q3: Are custom hooks reusable across projects?
Absolutely! You can extract them to a shared library or npm package.