React Hooks: The Complete Guide with Examples and Best Practices (2026)

A React Hook is a special function that lets you use state, lifecycle features, and other React capabilities inside functional components — no class syntax required. Hooks transformed how developers write React applications when introduced in React 16.8, and they remain the standard approach in React 19.

This guide covers every built-in React Hook with practical examples, explains when to use each one, and shares best practices for writing clean, maintainable hook-based code.

Key takeaways:

  • React Hooks let you add state, side effects, and other React features to functional components.
  • React 19 includes 17 built-in hooks, plus newer APIs like useActionState and useOptimistic.
  • The most-used hooks are useState, useEffect, useContext, and useRef.
  • Custom hooks let you extract and reuse stateful logic across components.
  • Building a React UI? UXPin Merge lets designers prototype with your team’s actual React components, so what gets designed uses the same hooks and logic that ships to production.

Design UI with code-backed components.

Use the same components in design as in development. Keep UI consistency at scale.



Try UXPin Merge

What Is a React Hook?

A React Hook is a JavaScript function provided by the React library that allows you to “hook into” React’s internal mechanisms — state management, component lifecycle, context, and more — from within a functional component.

Before hooks, these capabilities were only available in class components. Hooks eliminated that limitation, making functional components the default approach for modern React development.

What Does a Hook Do?

Each hook serves a specific purpose:

  • State hooks (useState, useReducer) — Manage component-level data that triggers re-renders when updated.
  • Effect hooks (useEffect, useLayoutEffect) — Run side effects like API calls or DOM manipulation.
  • Context hooks (useContext) — Access shared state without prop drilling.
  • Ref hooks (useRef, useImperativeHandle) — Reference DOM elements or persist values across renders.
  • Performance hooks (useMemo, useCallback, useTransition, useDeferredValue) — Optimize rendering.

Why Were Hooks Introduced?

Hooks were introduced in React 16.8 to solve several key problems:

  1. Class component complexity — Classes required boilerplate (this binding, lifecycle methods) that made code harder to maintain.
  2. Reusing stateful logic — Before hooks, sharing logic required render props and HOCs, creating deeply nested trees.
  3. Scattered related logic — In class components, setup and cleanup logic lived in different lifecycle methods. Hooks colocate related logic.

Hooks are backwards-compatible — you can adopt them incrementally without rewriting class components.

All Built-in React Hooks

As of React 19, there are 17 built-in hooks. Here’s every one with examples.

1. useState

useState is the most fundamental hook. It declares a state variable and a function to update it.

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

When to use: Any time a component needs to track and update data — form inputs, toggles, counters, selected items.

2. useEffect

useEffect runs side effects after render. It replaces componentDidMount, componentDidUpdate, and componentWillUnmount.

import React, { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => setUser(data));
  }, [userId]);

  return user ? <h1>{user.name}</h1> : <p>Loading...</p>;
}

When to use: Data fetching, subscriptions, timers, DOM manipulation, and any side effect after render.

3. useContext

useContext reads a value from a React context without prop drilling.

import React, { useContext, createContext } from 'react';

const ThemeContext = createContext('light');

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>Styled Button</button>;
}

When to use: Theme data, authenticated user info, locale settings, or any shared data.

4. useReducer

useReducer manages complex state logic with a reducer function, similar to Redux patterns.

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: throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });
  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </div>
  );
}

When to use: Complex state transitions or when multiple sub-values are updated together.

5. useMemo

useMemo caches the result of an expensive computation, recalculating only when dependencies change.

import React, { useMemo } from 'react';

function ExpensiveList({ items, filter }) {
  const filtered = useMemo(
    () => items.filter(item => item.includes(filter)),
    [items, filter]
  );
  return <ul>{filtered.map(i => <li key={i}>{i}</li>)}</ul>;
}

When to use: Expensive calculations, large list filtering, or derived data.

6. useRef

useRef returns a mutable ref object that persists across renders without triggering re-renders.

import React, { useRef } from 'react';

function TextInput() {
  const inputRef = useRef(null);
  const focusInput = () => inputRef.current.focus();
  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={focusInput}>Focus Input</button>
    </div>
  );
}

When to use: DOM element access, storing previous values, interval IDs, or mutable values that shouldn’t cause re-renders.

7. useCallback

useCallback returns a memoized callback function, preventing unnecessary child component re-renders.

import React, { useCallback, useState } from 'react';

function Parent() {
  const [count, setCount] = useState(0);
  const handleClick = useCallback(() => {
    setCount(c => c + 1);
  }, []);
  return <Child onClick={handleClick} />;
}

When to use: When passing callbacks to optimized child components wrapped in React.memo.

8. useId

useId generates a unique ID that’s stable across server and client renders — critical for accessibility.

import React, { useId } from 'react';

function FormField() {
  const id = useId();
  return (
    <div>
      <label htmlFor={id}>Email</label>
      <input id={id} type="email" />
    </div>
  );
}

When to use: Generating accessible IDs for form elements and ARIA attributes.

9. useTransition

useTransition marks state updates as non-urgent, keeping the UI responsive during expensive re-renders.

import React, { useState, useTransition } from 'react';

function SearchResults() {
  const [query, setQuery] = useState('');
  const [isPending, startTransition] = useTransition();

  const handleChange = (e) => {
    startTransition(() => setQuery(e.target.value));
  };

  return (
    <div>
      <input onChange={handleChange} />
      {isPending ? <p>Updating...</p> : <Results query={query} />}
    </div>
  );
}

When to use: Search-as-you-type, tab switching, or any update where UI responsiveness matters.

10. useDeferredValue

useDeferredValue defers updating a value until the browser has time, similar to debouncing but integrated into React’s rendering pipeline.

import React, { useState, useDeferredValue } from 'react';

function Search() {
  const [input, setInput] = useState('');
  const deferredInput = useDeferredValue(input);
  return (
    <div>
      <input value={input} onChange={e => setInput(e.target.value)} />
      <HeavyList filter={deferredInput} />
    </div>
  );
}

When to use: When a child component does expensive rendering based on frequently changing input.

11. useLayoutEffect

useLayoutEffect fires synchronously after DOM mutations but before browser paint. Use it for layout measurements.

import React, { useLayoutEffect, useRef } from 'react';

function Tooltip({ children }) {
  const ref = useRef();
  useLayoutEffect(() => {
    const { height } = ref.current.getBoundingClientRect();
    // Adjust position based on measured height
  });
  return <div ref={ref}>{children}</div>;
}

When to use: Measuring DOM elements, scroll position calculations, or preventing visual flicker.

12. useImperativeHandle

useImperativeHandle customizes the value exposed to parent components via ref with forwardRef.

import React, { useImperativeHandle, forwardRef, useRef } from 'react';

const CustomInput = forwardRef((props, ref) => {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => inputRef.current.focus(),
    clear: () => { inputRef.current.value = ''; }
  }));
  return <input ref={inputRef} />;
});

When to use: Building reusable component libraries that need to expose a controlled API.

13. useDebugValue

useDebugValue displays a label for custom hooks in React DevTools.

import { useDebugValue, useState } from 'react';

function useOnlineStatus() {
  const [isOnline, setIsOnline] = useState(true);
  useDebugValue(isOnline ? 'Online' : 'Offline');
  return isOnline;
}

When to use: Shared custom hooks where DevTools visibility aids debugging.

14. useInsertionEffect

useInsertionEffect fires before DOM mutations — designed specifically for CSS-in-JS libraries to inject styles before layout calculations.

When to use: Almost never in application code. This is a specialized hook for CSS-in-JS library authors.

15. useSyncExternalStore

useSyncExternalStore subscribes to external data stores in a way compatible with React’s concurrent features.

import { useSyncExternalStore } from 'react';

function useWindowWidth() {
  return useSyncExternalStore(
    (callback) => {
      window.addEventListener('resize', callback);
      return () => window.removeEventListener('resize', callback);
    },
    () => window.innerWidth
  );
}

When to use: Integrating with external state management libraries or browser APIs.

React 19 Additions: useActionState and useOptimistic

React 19 introduces two new hooks:

  • useActionState — Manages the state of form actions, providing pending state and the last returned result.
  • useOptimistic — Shows optimistic UI state during async operations, reverting if the operation fails.

These hooks reflect React’s movement toward better handling of async operations and server-side rendering patterns.

Custom Hooks: Reusing Stateful Logic

Custom hooks extract component logic into reusable functions. Any function starting with use that calls other hooks is a custom hook.

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(res => res.json())
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false));
  }, [url]);

  return { data, loading, error };
}

Custom hooks are the primary mechanism for sharing logic between components without altering the component tree.

React Hooks Best Practices

  1. Call hooks at the top level — Never inside loops, conditions, or nested functions.
  2. Only call hooks from React functions — Use them in functional components or custom hooks.
  3. Keep effects focused — One useEffect per concern.
  4. Specify complete dependency arrays — Use the react-hooks/exhaustive-deps ESLint rule.
  5. Don’t over-optimize — Use useMemo and useCallback only when you’ve identified a real performance problem.
  6. Extract custom hooks early — If a component has complex hook logic, extract it into a named custom hook.
  7. Clean up side effects — Always return a cleanup function from useEffect for subscriptions, timers, or event listeners.

React Hooks and Modern Design Workflows

Hooks shape how components behave — and that behavior matters for design. When designers prototype with static mockups, the interactive logic developers add with hooks (state transitions, loading states, validation feedback) is invisible until development.

UXPin Merge solves this by letting designers work with real React components — hooks and all. A button that uses useState to manage its active state behaves exactly the same on the design canvas as in production.

Teams using Merge report up to 50% reduction in engineering time compared to static-to-code handoff workflows. You can bring your team’s own React components via the Merge CLI tool or connect libraries like MUI, shadcn/ui, or Bootstrap.

Try UXPin Merge free

Design UI with code-backed components.

Use the same components in design as in development. Keep UI consistency at scale.



Try UXPin Merge

Frequently Asked Questions About React Hooks

What are React Hooks?

React Hooks are special functions that let you use state, side effects, context, and other React features inside functional components. They were introduced in React 16.8 and are the standard approach for writing React components.

How many hooks are in React?

React 19 provides 17 built-in hooks: useState, useEffect, useContext, useReducer, useMemo, useRef, useCallback, useId, useTransition, useDeferredValue, useLayoutEffect, useImperativeHandle, useDebugValue, useInsertionEffect, useSyncExternalStore, useActionState, and useOptimistic.

What is the difference between useState and useReducer?

useState is simpler and ideal for independent pieces of state. useReducer is better for complex state logic where the next state depends on the previous state.

When should I use useMemo vs useCallback?

useMemo caches the result of a computation. useCallback caches the function itself. Use useMemo for expensive derived data; use useCallback for stable callback references.

Can I create my own React Hooks?

Yes. Custom hooks are regular JavaScript functions that start with “use” and call other hooks. They’re the primary mechanism for extracting and sharing stateful logic.

What are the rules of React Hooks?

Two main rules: (1) Only call hooks at the top level — never inside loops, conditions, or nested functions. (2) Only call hooks from React functional components or custom hooks.


Use a single source of truth for design and development. Discover Merge

Logos

by Aneeqa Khan on 13th April, 2026

Software Engineer and enthusiast of learning and developing applications that adds value to the organization and environment as well. Experienced in developing applications in ReactJS and mobile applications in React Native.

Still hungry for the design?

UXPin is a product design platform used by the best designers on the planet. Let your team easily design, collaborate, and present from low-fidelity wireframes to fully-interactive prototypes.

Start your free trial

These e-Books might interest you