Writing Custom Hooks
In this tutorial, we will learn how to write custom Hooks in React 19. Custom Hooks allow you to extract reusable logic from components and share it across your application in a clean and predictable way.
What We Will Learn
- What custom Hooks are and why they exist
- How custom Hooks work under the hood
- How to name and structure a custom Hook
- Writing a simple reusable custom Hook
- Using custom Hooks with the new
useAPI - Best practices for custom Hooks in React 19
What Is a Custom Hook
A custom Hook is simply a JavaScript function that:
- Starts with the word
use - Calls other Hooks inside it
- Encapsulates reusable logic
Think of a custom Hook like a reusable recipe. Instead of rewriting the same logic in multiple components, you write it once and reuse it anywhere.
Why Custom Hooks Are Important
Without custom Hooks, components can become:
- Large
- Hard to read
- Hard to reuse
Custom Hooks help by:
- Keeping components small and focused
- Sharing logic without copying code
- Making code easier to test and reason about
Basic Rules of Custom Hooks
Custom Hooks follow the same rules as built in Hooks:
- Only call Hooks at the top level
- Only call Hooks from React components or other Hooks
- Always start the function name with
use
React uses the use prefix to understand Hook behavior.
A Simple Example Without a Custom Hook
Imagine you need to track window width in multiple components.
// app/page.tsx
"use client";
import { useState, useEffect } from "react";
export default function Page() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
function handleResize() {
setWidth(window.innerWidth);
}
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return <p>Window width: {width}</p>;
}This works but the logic is locked inside the component.
Extracting Logic Into a Custom Hook
Now we move the logic into a custom Hook.
// hooks/use-window-width.ts
"use client";
import { useState, useEffect } from "react";
export function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
function handleResize() {
setWidth(window.innerWidth);
}
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return width;
}Using the Custom Hook in a Component
// app/page.tsx
"use client";
import { useWindowWidth } from "@/hooks/use-window-width";
export default function Page() {
const width = useWindowWidth();
return <p>Window width: {width}</p>;
}Now the component is clean and focused on rendering.
Custom Hooks and the use API
In React 19, the use API allows Hooks to consume promises and resources directly.
You can combine custom Hooks with use to encapsulate async logic.
Example Custom Hook Using use
// hooks/use-user.ts
import { use } from "react";
async function fetchUser() {
const res = await fetch("https://api.example.com/user");
return res.json();
}
export function useUser() {
return use(fetchUser());
}Using It in a Component
// app/page.tsx
import { useUser } from "@/hooks/use-user";
export default function Page() {
const user = useUser();
return <p>Hello {user.name}</p>;
}The component stays simple while the Hook handles data logic.
Custom Hooks With React Actions
Custom Hooks are also great for wrapping React Actions logic.
// hooks/use-counter.ts
"use client";
import { useActionState } from "react";
function increment(count: number) {
return count + 1;
}
export function useCounter() {
const [count, incrementAction] = useActionState(increment, 0);
return {
count,
incrementAction,
};
}// app/page.tsx
"use client";
import { useCounter } from "@/hooks/use-counter";
export default function Page() {
const { count, incrementAction } = useCounter();
return (
<form action={incrementAction}>
<p>Count: {count}</p>
<button type="submit">Increment</button>
</form>
);
}Best Practices for Custom Hooks
- Keep custom Hooks focused on one responsibility
- Return values instead of JSX
- Avoid side effects unless necessary
- Name Hooks clearly based on what they do
- Prefer composition of Hooks over large Hooks
Good Hooks feel like small building blocks.
Folder Structure Example
/hooks/
use-window-width.ts
use-user.ts
use-counter.ts
This keeps reusable logic separate from UI.
Conclusion
Custom Hooks are one of the most powerful tools in React 19.
They allow you to share logic, simplify components, and embrace modern patterns like the use API and React Actions.
If a piece of logic feels reusable, it probably belongs in a custom Hook. Write once, reuse everywhere, and keep your components clean.