How many hooks are in React 19?
A Complete List of Hooks Available in React 19
There are a total of 19 hooks, as listed below:
- State Management Hooks
useStateuseReducer
- Context Hook
useContext
- Reference Hooks
useRefuseImperativeHandle
- Side Effect Hooks
useEffectuseLayoutEffectuseInsertionEffect
- Performance Optimization Hooks
useMemouseCallback
- Scheduling Hooks
useTransitionuseDeferredValue
- Other Hooks
useIduseSyncExternalStoreuseDebugValue
- New Hooks in React 19
useuseActionStateuseFormStatususeOptimistic
useState
useState allow function components to have and manage their own state, enabling components to response to changes and re-render.
Here's an example of how to use it:
import React, { useState } from 'react';
export default function SimpleCounter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
useReducer
useReducer is used for managing more complex state logic. It is similar to useState but is better suited for handling multiple related state values or when the next state depends on the previous state.
Here's an example of how to use it:
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
export default function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
Count: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
}
useContext
useContext is used to share data within a component tree without having to pass props through each component layer. It allows components to directly access context defined in a parent component.
Here's an example of how to use it:
import React, { createContext, useContext, useState } from 'react';
const ThemeContext = createContext('light');
function DisplayTheme() {
const theme = useContext(ThemeContext);
return <div>Current theme: {theme}</div>;
}
export default function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={theme}>
<DisplayTheme />
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
</ThemeContext.Provider>
);
}
useRef
useRef is used to create a mutable reference that persists throughout the component's lifecycle without triggering re-renders. It's commonly used to store references to DOM elements or any mutable value that doesn't require re-rendering.
Here's an example of how to use it:
import React, { useRef } from 'react';
export default function FocusInput() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus Input</button>
</div>
);
}
useImperativeHandle
useImperativeHandle allows you to customize the instance value that is exposed to the parent component when using a ref. This Hook is typically used with forwardRef to expose custom methods or properties to the parent component.
Here's an example in TypeScript for clarity:
import React, { useRef, useImperativeHandle, forwardRef, useState } from 'react';
type CounterRef = {
increment: () => void;
getCount: () => number;
};
const CustomCounter = forwardRef<CounterRef>((props, ref) => {
const [count, setCount] = useState(0);
// The ref holds the object returned by the second argument
useImperativeHandle(ref, () => ({
increment: () => setCount(count + 1),
getCount: () => count
}));
return <div>Count: {count}</div>;
});
export default function App() {
const counterRef = useRef<CounterRef>(null);
const handleClick = () => {
if (counterRef.current) {
counterRef.current.increment();
console.log('Current count:', counterRef.current.getCount());
}
};
return (
<div>
<CustomCounter ref={counterRef} />
<button onClick={handleClick}>Increment and Log Count</button>
</div>
);
}
useEffect
useEffect is used to perform side effects in function components, such as data fetching, subscriptions, or manually modifying the DOM. It runs after the component renders, and its execution timing can be controlled through a dependency array.
Here's an example of how to use it:
import React, { useState, useEffect } from 'react';
export default function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount((prevCount) => prevCount + 1);
}, 1000);
return () => {
clearInterval(timer);
};
}, []);
return <div>Timer: {count} seconds</div>;
}
useLayoutEffect
useLayoutEffect works the same as useEffect, with the difference in when it fires. It runs synchronously after all DOM changes but before the browser has painted. Because this process is synchronous, time-consuming operations in useLayoutEffect can block page rendering. Generally, useEffect should be preferred unless you need to access or modify the DOM before the paint.
Here's an example of how to use it:
import React, { useLayoutEffect } from 'react';
export default function LogDOMInfo() {
useLayoutEffect(() => {
const mainElement = document.querySelector('main');
if (mainElement) {
console.log('Main element width:', mainElement.clientWidth);
console.log('Main element height:', mainElement.clientHeight);
}
}, []);
return (
<main>
<h1>Hello, useLayoutEffect!</h1>
<p>Check the console for main element dimensions.</p>
</main>
);
}
useInsertionEffect
useInsertionEffect is a React Hook designed specifically for CSS-in-JS libraries. It fires synchronously after DOM changes but before layout effects read the new layout. This Hook is primarily used to inject styles into the DOM before any layout shifts occur, avoiding layout jank.
Here's an example of how to use it:
import React, { useState, useInsertionEffect } from 'react';
// Simulate a simple CSS-in-JS
const injectStyle = (css: string) => {
const style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
return style;
};
export default function DynamicStyle() {
const [color, setColor] = useState('red');
useInsertionEffect(() => {
const style = injectStyle(`
.dynamic-text {
color: ${color};
font-size: 24px;
font-weight: bold;
}
`);
return () => {
document.head.removeChild(style);
};
}, [color]);
return (
<div>
<p className="dynamic-text">This text has dynamic styling</p>
<button onClick={() => setColor(color === 'red' ? 'blue' : 'red')}>
Toggle Color
</button>
</div>
);
}
useMemo
useMemo is used to memoize the result of a calculation. It takes a function and dependency array, and only recalculates the result when dependencies change. This is useful for optimizing values that are computationally expensive.
Here's an example of how to use it:
import React, { useState, useMemo } from 'react';
function expensiveCalculation(num: number): number {
console.log('Calculating...');
for (let i = 0; i < 1000000000; i++) {
num += 1;
}
return num;
}
export default function Calculator() {
const [num, setNum] = useState(1);
// expensiveResult will only be recalculated when num changes, if num remains the same, it won't be recalculated
const expensiveResult = useMemo(() => expensiveCalculation(num), [num]);
return (
<div>
<h2>Expensive Calculation Result: {expensiveResult}</h2>
<button onClick={() => setNum(num + 1)}>Change Calculation Input</button>
</div>
);
}
useCallback
useCallback is used to memoize a function definition. It takes a callback and a dependency array and returns a new function only if dependenciees change. This is useful for optimizing functions passed as props to child components.
Here's an example of how to use it:
In the following example, you can see Button Button "Button 2" rendered in console, but not Button "Button 1" rendered. This is because useCallback's dependency array hasn't changed, so it returns the same function instead of recreating it each time.
import React, { useState, useCallback, memo } from 'react';
const ExpensiveButton = memo(
({ onClick, label }: { onClick: () => void; label: string }) => {
console.log(`Button "${label}" rendered`);
return <button onClick={onClick}>{label}</button>;
},
);
export default function CallbackExample() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
const incrementCount1 = useCallback(() => {
setCount1((prevCount) => prevCount + 1);
}, []);
const incrementCount2 = () => {
setCount2((prevCount) => prevCount + 1);
};
console.log('Parent component rendered');
return (
<div>
<h2>With useCallback: {count1}</h2>
<ExpensiveButton onClick={incrementCount1} label="Button 1" />
<h2>Without useCallback: {count2}</h2>
<ExpensiveButton onClick={incrementCount2} label="Button 2" />
<p>Check the console to see which components are re-rendering.</p>
</div>
);
}
useTransition
useTransition provides a flag to indicate the loading state, which developers can use to improve the user experience, espencially when handling slower updates that may cause the UI to lag.
Here's an example of how to use it:
import React, { useState, useTransition } from 'react';
function SlowList({ text }: { text: string }) {
const items = [];
for (let i = 0; i < 500; i++) {
items.push(<li key={i}>{text}</li>);
}
return <ul>{items}</ul>;
}
export default function TransitionExample() {
const [text, setText] = useState('');
const [deferredText, setDeferredText] = useState('');
const [isPending, startTransition] = useTransition();
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
setText(e.target.value);
startTransition(() => {
setDeferredText(e.target.value);
});
}
return (
<div>
<input value={text} onChange={handleChange} />
<p>Typed text: {text}</p>
{isPending ? (
<p>Updating list...</p>
) : (
<SlowList text={deferredText} />
)}
</div>
);
}
useDeferredValue
useDeferredValue is used to delay updating a certain value. It takes a value and returns a new copy of that value, which updates more slowly. This is useful for postponing the rendering of more computationally expensive parts.
In the example below, if you type or delete content quickly, the value of deferredText will take a moment to update.

import React, { useState, useDeferredValue, memo } from 'react';
const SlowList = memo(function SlowList({ text }: { text: string }) {
console.log('Rendering SlowList');
const items = [];
for (let i = 0; i < 10000; i++) {
items.push(<li key={i}>{text}</li>);
}
return <ul>{items}</ul>;
});
export default function DeferredValueExample() {
const [text, setText] = useState('');
const deferredText = useDeferredValue(text);
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
setText(e.target.value);
}
return (
<div>
<input
value={text}
onChange={handleChange}
placeholder="Type here..."
style={{ fontSize: '18px', padding: '5px', width: '200px' }}
/>
<p>Immediate text: {text}</p>
<p style={{ color: text === deferredText ? 'black' : 'red' }}>
Deferred text: {deferredText}
</p>
<SlowList text={text} />
</div>
);
}
useId
useId is used to generate a unique ID.
Here's an example of how to use it:

import React, { useId } from 'react';
function LabeledInput({ label }: { label: string }) {
const id = useId();
return (
<div>
<label htmlFor={id}>{label}</label>
<input id={id} type="text" />
</div>
);
}
export default function UniqueIdExample() {
return (
<div>
<h2>Form with Unique IDs</h2>
<LabeledInput label="First Name" />
<LabeledInput label="Last Name" />
<LabeledInput label="Email" />
</div>
);
}
useSyncExternalStore
useSyncExternalStore is used to subscribe to an external data source, facilitating integration between React and external state libraries.
Here's an example of how to use it:
import React, { useSyncExternalStore } from 'react';
// Simulate an external store
const createStore = (initialState: number) => {
let state = initialState;
const listeners = new Set<() => void>();
return {
subscribe: (listener: () => void) => {
listeners.add(listener);
return () => listeners.delete(listener);
},
getSnapshot: () => state,
increment: () => {
state++;
listeners.forEach(listener => listener());
}
};
};
const store = createStore(0);
function Counter() {
const count = useSyncExternalStore(
store.subscribe,
store.getSnapshot
);
return (
<div>
<p>Count: {count}</p>
<button onClick={store.increment}>Increment</button>
</div>
);
}
export default function ExternalStoreExample() {
return (
<div>
<h2>External Store Example</h2>
<Counter />
<Counter />
</div>
);
}
useDebugValue
useDebugValue is used to add labels for custom Hooks in React DevTools. It allows developers to provide more meaning full debug information for a custom Hook, making it easier to understand the Hook's current state when inspecting components in React DevTools.
Here's an example of how to use it:

import React, { useState, useEffect, useDebugValue } from 'react';
// Custom Hook : useOnlineStatus
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
const handleOnline = () => setIsOnline(true);
const handleOffline = () => setIsOnline(false);
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
// Using useDebugValue to add debug information
useDebugValue(isOnline ? 'Online' : 'Offline');
return isOnline;
}
function StatusIndicator() {
const isOnline = useOnlineStatus();
return (
<div>
<h2>Network Status</h2>
<p>You are {isOnline ? 'online' : 'offline'}</p>
</div>
);
}
export default function DebugValueExample() {
return (
<div>
<h1>useDebugValue Example</h1>
<StatusIndicator />
</div>
);
}
use
use is a new React Hook that works with Suppense to wait for resource loading during the redering process.
Here's an example of how to use it:

import React, { use, Suspense } from 'react';
// Simulate asynchronous data loading
const fetchComments = () =>
new Promise((resolve) =>
setTimeout(() => resolve(['Comment 1', 'Comment 2', 'Comment 3']), 1000),
);
function Comments() {
const comments = use(fetchComments());
return (
<ul>
{comments.map((comment, index) => (
<li key={index}>{comment}</li>
))}
</ul>
);
}
export default function UseExample() {
return (
<div>
<h1>Comments</h1>
<Suspense fallback={<div>Loading comments...</div>}>
<Comments />
</Suspense>
</div>
);
}
useActionState
useActionState is used to manage the state of form submissions. Traditionally, handling form data requires manually creating multiple state, but with useActionState, you can manage these without manual state handling.
Here's an example of how to use it:
'use client';
import React, { useActionState } from 'react';
// Simulate a server action
async function submitForm(prevState, formData) {
// Simalate network request
await new Promise((resolve) => setTimeout(resolve, 1000));
const email = formData.get('email');
if (!email.includes('@')) {
return { error: 'Invalid email address' };
}
return { success: true, message: 'Form submitted successfully' };
}
export default function ActionStateExample() {
const [state, formAction, pending] = useActionState(submitForm, {
error: null,
success: false,
message: '',
});
return (
<div>
<h1>useActionState Example</h1>
<form action={formAction}>
<div>
<label htmlFor="email">Email:</label>
<input type="email" id="email" name="email" required />
</div>
<button type="submit" disabled={pending}>
{pending ? 'Submitting...' : 'Submit'}
</button>
</form>
{state.error && <p style={{ color: 'red' }}>{state.error}</p>}
{state.success && <p style={{ color: 'green' }}>{state.message}</p>}
</div>
);
}
useFormStatus
useFormStatus is used to retrieve the status information of the nearest ancestor <form> element. For example, it can be utilized to adjust the style of a SubmitButton during form submission.
Here's an example of how to use it:
import React from 'react';
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button
type="submit"
disabled={pending}
className={`px-4 py-2 rounded ${pending ? 'bg-gray-400' : 'bg-blue-500 hover:bg-blue-600'} text-white`}
>
{pending ? 'Submitting...' : 'Submit'}
</button>
);
}
function handleSubmit(formData) {
// Simulate an asynchronous operation
return new Promise((resolve) => {
setTimeout(() => {
console.log('Form submitted with:', Object.fromEntries(formData));
resolve(null);
}, 2000);
});
}
export default function FormStatusExample() {
return (
<form action={handleSubmit} className="space-y-4">
<div>
<label
htmlFor="name"
className="block text-sm font-medium text-gray-700"
>
Name:
</label>
<input
type="text"
id="name"
name="name"
required
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50"
/>
</div>
<div>
<label
htmlFor="email"
className="block text-sm font-medium text-gray-700"
>
Email:
</label>
<input
type="email"
id="email"
name="email"
required
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50"
/>
</div>
<SubmitButton />
</form>
);
}
useOptimistic
useOptimistic is used to implement optimistic updates, allowing you to immediately update the UI while waiting for an asynchronous operation to complete, providing a faster user experience.
Here's an example of how to use it:

import { useOptimistic, useState, useRef } from 'react';
async function deliverMessage(message) {
await new Promise((resolve) => setTimeout(resolve, 2000));
return message;
}
function Thread({ messages, sendMessage }) {
const formRef = useRef();
async function formAction(formData) {
addOptimisticMessage(formData.get('message'));
formRef.current.reset();
await sendMessage(formData);
}
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
(state, newMessage) => [
...state,
{
text: newMessage,
sending: true,
},
],
);
return (
<>
{optimisticMessages.map((message, index) => (
<div key={index}>
{message.text}
{!!message.sending && <small> (Sending...)</small>}
</div>
))}
<form action={formAction} ref={formRef}>
<input type="text" name="message" placeholder="Hello!" />
<button type="submit">Send</button>
</form>
</>
);
}
export default function App() {
const [messages, setMessages] = useState([
{ text: 'Hello there!', sending: false, key: 1 },
]);
async function sendMessage(formData) {
const sentMessage = await deliverMessage(formData.get('message'));
setMessages((messages) => [...messages, { text: sentMessage }]);
}
return <Thread messages={messages} sendMessage={sendMessage} />;
}