React Hooks Cheatsheet – All Hooks Explained with Syntax
Master React Hooks with this complete, up-to-date cheatsheet for React 19.2. Includes every React Hook — from useState and useEffect to useFormStatus and useOptimistic — with clear explanations, syntax, and examples.
Perfect quick reference for React developers, from beginner to pro.
React Hooks Cheatsheet(Updated for React 19.2)
The most fundamental React Hook for managing state inside functional components. It returns an array with a state variable and a setter function. The initial value is set only on the first render, and React preserves the value between renders. Ideal for managing UI states like toggles, form inputs, counters, or visibility flags.
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const increment = () => setCount(prev => prev + 1);
const reset = () => setCount(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={reset}>Reset</button>
</div>
);
}An alternative to useState for managing more complex or related pieces of state. It follows a reducer pattern (similar to Redux) where you define a reducer function that handles different action types. Great for complex UIs, multi-step forms, or scenarios with intricate state transitions.
import { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
}Runs side effects after every render by default (or when dependencies change). Side effects include data fetching, DOM updates, subscriptions, or event listeners. The cleanup function runs before the effect re-runs or when the component unmounts, ensuring proper resource management.
import { useEffect, useState } from 'react';
function FetchUser() {
const [user, setUser] = useState(null);
useEffect(() => {
async function getUser() {
const res = await fetch('https://jsonplaceholder.typicode.com/users/1');
const data = await res.json();
setUser(data);
}
getUser();
}, []);
return user ? <p>{user.name}</p> : <p>Loading...</p>;
}Effect Hooks: useLayoutEffect
Works just like useEffect but fires synchronously after all DOM mutations and before the browser paints. It’s useful when you need to measure DOM elements, perform layout calculations, or make visual adjustments that must happen before the browser paints the screen.
import { useLayoutEffect, useRef, useState } from 'react';
function Box() {
const boxRef = useRef();
const [boxWidth, setBoxWidth] = useState(0);
useLayoutEffect(() => {
const width = boxRef.current.getBoundingClientRect().width;
setBoxWidth(width);
}, []);
return <div ref={boxRef}>Box width: {boxWidth}px</div>;
}Effect Hooks: useInsertionEffect
Runs before any DOM mutations. It's primarily used by CSS-in-JS or styling libraries to insert styles before the browser paints. It ensures visual consistency and avoids flickering during hydration or style injection.
import { useInsertionEffect } from 'react';
function ThemedComponent() {
useInsertionEffect(() => {
const style = document.createElement('style');
style.textContent = `.highlight { color: red; }`;
document.head.appendChild(style);
return () => document.head.removeChild(style);
}, []);
return <p className="highlight">Styled before paint!</p>;
}Lets components access values from a React Context directly, without prop drilling. Perfect for global data like theme, authentication, or language. When the context value changes, all consuming components re-render.
import { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
function Child() {
const theme = useContext(ThemeContext);
return <p>Current theme: {theme}</p>;
}
function App() {
return (
<ThemeContext.Provider value="dark">
<Child />
</ThemeContext.Provider>
);
}Creates a mutable object with a .current property that persists across renders. It’s often used to directly access DOM elements or store any mutable value that doesn’t trigger a re-render when changed.
import { useRef } from 'react';
function InputFocus() {
const inputRef = useRef(null);
const handleFocus = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} type="text" placeholder="Focus me" />
<button onClick={handleFocus}>Focus</button>
</div>
);
}Ref Hooks: useImperativeHandle
Customizes what gets exposed when using a ref with forwardRef. You can define controlled APIs for a child component so the parent can call specific functions safely.
import { useImperativeHandle, useRef, forwardRef } from 'react';
const Input = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus()
}));
return <input ref={inputRef} />;
});
function App() {
const inputRef = useRef();
return <><Input ref={inputRef} /><button onClick={() => inputRef.current.focus()}>Focus Input</button></>;
}Caches the result of an expensive computation and only recomputes when dependencies change. Prevents unnecessary recalculations in performance-sensitive applications like filtering or sorting large datasets.
import { useMemo, useState } from 'react';
function FilteredList({ items }) {
const [query, setQuery] = useState('');
const filteredItems = useMemo(() => {
return items.filter(item => item.toLowerCase().includes(query.toLowerCase()));
}, [query, items]);
return (
<div>
<input value={query} onChange={e => setQuery(e.target.value)} />
<ul>{filteredItems.map(i => <li key={i}>{i}</li>)}</ul>
</div>
);
}Returns a memoized version of a callback function that only changes if dependencies change. It’s especially useful when passing callbacks to memoized or pure child components to avoid unnecessary re-renders.
import { useCallback, useState } from 'react';
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(prev => prev + 1);
}, []);
return <Child onClick={handleClick} count={count} />;
}
function Child({ onClick, count }) {
return <button onClick={onClick}>Clicked {count} times</button>;
}Marks updates as non-urgent, allowing React to prioritize important updates. Ideal for complex UIs like search or tab changes, where immediate feedback is more important than rendering the final result instantly.
import { useTransition, useState } from 'react';
function SearchList({ items }) {
const [query, setQuery] = useState('');
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
const value = e.target.value;
startTransition(() => setQuery(value));
};
const filtered = items.filter(i => i.includes(query));
return (
<div>
<input onChange={handleChange} placeholder="Search..." />
{isPending && <p>Updating...</p>}
<ul>{filtered.map(i => <li key={i}>{i}</li>)}</ul>
</div>
);
}Performance Hooks (React 18+): useDeferredValue
Lets you defer re-rendering of non-urgent parts of your UI. Great for heavy components that depend on fast-changing inputs, such as search results or large lists.
import { useDeferredValue } from 'react';
function SearchResults({ query }) {
const deferredQuery = useDeferredValue(query);
const results = expensiveSearch(deferredQuery);
return <ul>{results.map(r => <li key={r}>{r}</li>)}</ul>;
}Performance Hooks (React 18+): useId
Generates a unique, consistent ID that works on both client and server. Commonly used for accessibility attributes like aria-labelledby or for associating labels with form inputs.
import { useId } from 'react';
function FormInput() {
const id = useId();
return (
<div>
<label htmlFor={id}>Email</label>
<input id={id} type="email" />
</div>
);
}Accesses the status of a form submission (pending, success, or error). Useful for disabling buttons or showing loaders while submitting forms.
function SubmitButton() {
const { pending } = useFormStatus();
return <button disabled={pending}>{pending ? 'Submitting...' : 'Submit'}</button>;
}Manages form state and feedback during server actions. Perfect for React 19’s Server Actions — it automatically tracks loading, success, and error states when submitting forms or performing mutations like creating or deleting data.
export default function FeedbackForm() {
const [state, formAction] = useActionState(async (prevState, formData) => {
const message = formData.get('message');
const result = await submitFeedback(message);
return { status: result.ok ? 'success' : 'error' };
}, { status: 'idle' });
return (
<form action={formAction}>
<textarea name="message" placeholder="Share your thoughts..." />
<button type="submit">
{state.status === 'idle' && 'Submit'}
{state.status === 'success' && 'Thanks!'}
{state.status === 'error' && 'Try Again'}
</button>
</form>
);
}React 19 Hooks: useOptimistic
Helps you provide instant feedback in the UI while waiting for a server update. Ideal for optimistic UI patterns where you update the interface first, then reconcile with the actual server response — like instantly showing a new comment before it's confirmed.
function Comments({ comments }) {
const [optimisticComments, addOptimisticComment] = useOptimistic(comments);
async function handleAddComment(text) {
addOptimisticComment([...optimisticComments, { id: Date.now(), text }]); // Instant UI feedback
await postCommentToServer(text); // Then sync with backend
}
return (
<div>
{optimisticComments.map(c => <p key={c.id}>{c.text}</p>)}
<button onClick={() => handleAddComment('Great post!')}>Add Comment</button>
</div>
);
}React 19 Hooks: use
Allows you to consume promises or async data directly in components. Suspends rendering until the promise resolves — simplifying data fetching in server and async components.
const user = use(fetchUser());
return <p>{user.name}</p>;Custom hooks let you encapsulate reusable logic by combining built-in hooks. They make complex behavior (like data fetching, form handling, or persistence) easily shareable between components. Always prefix the function name with 'use' so React can correctly track hook usage.
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
const saved = localStorage.getItem(key);
return saved ? JSON.parse(saved) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
// Usage
function ThemeToggle() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
return <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>{theme}</button>;
}