Custom Hooks
Extract reusable stateful logic into your own use... functions.
Syntax
function useToggle(initial) { /* ... */ }A custom hook is a function whose name starts with use and which calls other hooks. It lets you extract and reuse stateful logic across components without duplicating it.
Guidelines
- Name it starting with
useso the rules of hooks apply. - It can call
useState,useEffectand other hooks. - Return whatever the caller needs — a value, a tuple, or an object.
Custom hooks share logic, not state — each component that calls the hook gets its own independent state.
Example
import { useState, useEffect } from 'react';
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const onResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', onResize);
return () => window.removeEventListener('resize', onResize);
}, []);
return width;
}
function Widget() {
const width = useWindowWidth();
return <p>Window is {width}px wide</p>;
}When to use it
- A team extracts their data-fetching boilerplate into a useFetch hook so every feature component gets loading, error, and data state with a single line call.
- A form library ships a useField hook that manages value, touched, and error state for each input, eliminating repetition across every form in the codebase.
- A realtime feature wraps WebSocket connection logic in a useWebSocket hook so any component can subscribe to a channel without knowing the connection lifecycle.
More examples
useLocalStorage custom hook
A custom hook that syncs state to localStorage, initialising lazily from storage and writing back on every update.
import { useState } from 'react';
function useLocalStorage(key, initial) {
const [value, setValue] = useState(() => {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initial;
} catch {
return initial;
}
});
const set = v => {
setValue(v);
localStorage.setItem(key, JSON.stringify(v));
};
return [value, set];
}
// Usage
// const [theme, setTheme] = useLocalStorage('theme', 'light');useFetch data-fetching hook
Encapsulates fetch, loading, and error state in a reusable hook so components focus only on rendering.
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetch(url)
.then(r => r.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}
// Usage
// const { data, loading } = useFetch('/api/posts');useMediaQuery responsive hook
Wraps the MediaQueryList API in a hook so any component can reactively adapt to viewport changes.
import { useState, useEffect } from 'react';
function useMediaQuery(query) {
const [matches, setMatches] = useState(
() => window.matchMedia(query).matches
);
useEffect(() => {
const mq = window.matchMedia(query);
const handler = e => setMatches(e.matches);
mq.addEventListener('change', handler);
return () => mq.removeEventListener('change', handler);
}, [query]);
return matches;
}
// Usage
// const isMobile = useMediaQuery('(max-width: 768px)');
Discussion