Custom Hooks
By Syncfusion®, Inc. · Last updated
What custom hooks are and why to use them, reusing logic across components, creating and naming hooks, using built-in hooks inside them, and useToggle and useForm examples.
What Are Custom Hooks?
A custom hook is a function that you write yourself, whose name starts with use, and that uses other hooks inside it. It lets you take some hook logic out of a component and use it again in other components.
For example, useToggle could hold the logic for "show or hide". Any component can then call useToggle() instead of writing that logic again.
Real-World Example
In a bookshop app, the book list, the order list and the author page all load data from the server. Each one needs data, loading and error state and a useEffect.
The team moves this code into one custom hook, useFetch(url). Each page now calls const { data, loading, error } = useFetch("/books") in one line.
A custom hook is a normal function that starts with use and calls other hooks. It shares logic, not data. Each component that uses it gets its own copy of the state.
Why Custom Hooks?
Without custom hooks, you often copy the same lines of state and effect code into many components.
- Less repeated code — Write the logic once and use it everywhere.
- Cleaner components — The component shows what it does, and the hook hides how it is done.
- One place to fix bugs — Fix the hook once, and every component that uses it is fixed.
- Easier to test — A small hook is easier to check than a big component.
Reusing Logic Across Components
Look at the two components below. Both have the same counter logic: a useState, an "add" function and a "reset" function.
The three logic lines are the same in both components. If you wanted to change how reset works, you would have to change it twice. A custom hook fixes this.
Creating Custom Hooks
To create a custom hook:
- Write a function whose name starts with
use, such asuseCounter. - Move the hook logic into it.
- Return what the component needs, such as the value and the functions.
Click the + buttons. Each counter changes on its own. Both components use useCounter, but each one gets its own count. The hook shares the logic, not the number.
Look at the last line of the hook:
return { count, add, reset };
The hook gives back an object with three things. The component picks them up as likes.count, likes.add and likes.reset.
One custom hook, useCounter, is used by two components. Each component gets its own count
useCounter() the logic, written once
LikeButton its own count: 3
CartCounter its own count: 5
The hook shares the logic. Each component that calls it gets its own separate state.
If two components must show the same number, a custom hook alone is not enough. Keep the state in their parent and pass it down, or use context.
Returning an Array or an Object
A custom hook can return any value. Two styles are common:
- An object —
return { count, add, reset };The component reads them by name. Good when there are many items. - An array —
return [isOn, toggle] as const;The component can pick its own names, likeuseState. Good for two items.
as const tells TypeScript that the array always has exactly these two items, in this order.
Naming Conventions
- Always start with
use—useCounter,useToggle,useForm. React and its tools use theusestart to check the rules of hooks. - Say what it does —
useWindowWidthis clear.useDatais not. - One hook, one job — If a hook does many things, split it into smaller hooks.
- One hook per file — Name the file after the hook:
useCounter.ts. Use.tswhen the hook returns no TSX.
If your function does not call any other hook, it is a normal helper function. Do not start its name with use.
Using Built-in Hooks Inside Custom Hooks
A custom hook can call any built-in hook: useState, useEffect, useRef, and so on. It can also call other custom hooks.
This hook uses useState and useEffect to keep track of the width of the window.
useWindowWidth hides useState and useEffect inside. App only gets a number
useWindowWidth()
useState holds the width
useEffect listens for resize inside the hook
width
App gets a number
A custom hook can use built-in hooks inside. The component only sees the result.
Drag the handle between the code and the preview to make the preview wider or narrower. The width updates as you drag.
App does not know about events or effects. It just calls useWindowWidth() and gets a number. All the hard work is inside the hook.
The return () => ... line inside the effect is a cleanup function. It stops listening when the component is removed. You will learn about it in the next lesson.
Toggle Hook Example
A toggle is a true/false value that flips. It is used for show/hide buttons, menus, dark mode, and more.
useToggle is called twice, so there are two separate true/false values. Details start hidden. Reviews start shown, because of useToggle(true).
Look at this line:
const [showDetails, toggleDetails] = useToggle();
Because the hook returns an array, you choose the names, just like with useState.
Form Handling Hook Example
Forms repeat the same code again and again: one state object, one change handler, and a reset. A useForm hook can hold all of it.
useForm: typing calls handleChange, which updates values; reset puts the start values back
Input boxes name="title"
useForm values, handleChange, reset
submit
Save the book
reset()
Empty boxes start values
The hook keeps the form values and gives the component everything it needs.
The component is short. It only says what the form has (title and author) and what to do on submit. The hook handles typing and resetting.
Look at the first line of the hook:
export function useForm<T extends Record<string, string>>(startValues: T) {
<T ...> makes the hook work with any form. T stands for "the shape of your form values". Here, T becomes { title: string; author: string }. So TypeScript knows that values.title exists.
Common mistakes
| Mistake | What you see | Fix |
|---|---|---|
Naming the hook without use, like counter() | Hook rules are not checked, and bugs are hard to find | Start the name with use |
| Expecting two components to share the same state | Changing one does not change the other | Each call gets its own state. Use props or context to share data |
Calling the custom hook inside an if | React error about hooks | Call it at the top of the component |
| Forgetting to return the values from the hook | The component gets undefined | Add return { ... } or return [ ... ] |
Returning an array without as const | TypeScript mixes up the types of the items | Write return [value, fn] as const; |
| One hook doing many unrelated jobs | Hard to read and reuse | Split it into small hooks with one job each |
Check Yourself
Exercises
What to Remember
- A custom hook is a function that starts with
useand calls other hooks. It shares logic between components. - Each component that calls the hook gets its own state.
- Return what the component needs: an object for many items, or an array with
as constfor two.
Next: Component Lifecycle