React Hooks Examples

Comprehensive examples of all React hooks including React 19 features

React 19 Hooks

Advanced Todo App

Comprehensive React 19 Hooks Demo

This example demonstrates how multiple React 19 hooks work together in a real-world application. It showcases a complete todo app with optimistic updates, server actions, form handling, and smooth UI transitions.

React 19 Hooks Used:

  • useTransition: Makes filter changes non-urgent, preventing UI blocking. Filter buttons respond immediately while the list update is non-blocking.
  • useOptimistic: Shows new todos instantly with a "(saving...)" label. The UI updates immediately before server confirmation for better perceived performance.
  • useActionState: Manages async form submission, errors, and success states in one unified place, replacing the need for separate useState calls.
  • useFormStatus: The submit button automatically knows when the form is submitting without needing to pass pending state as props. This enables better component composition.

Code Example:

import { useTransition, useOptimistic, useActionState } from 'react';

function TodoApp() {
  // useTransition: Non-urgent filter updates
  const [isPending, startTransition] = useTransition();
  
  // useOptimistic: Instant UI updates
  const [todos, addOptimistic] = useOptimistic(
    state.todos,
    (current, newTodo) => [...current, newTodo]
  );
  
  // useActionState: Form state + server action
  const [state, formAction] = useActionState(addTodo, initialState);
  
  // useFormStatus: Auto-detect form submission (in child)
  function handleFilterChange(filter) {
    startTransition(() => setFilter(filter));
  }
  
  return <form action={formAction}>...</form>;
}
Learn React 19 hooks
Build amazing apps
Share with community
3 active0 completed3 total

React useTransition Hook

What is useTransition?

useTransition lets you mark some state updates as non-urgent. Other state updates in your component will interrupt the transition, allowing urgent updates (like user input) to be handled immediately.

Key Features:

  • Marks state updates as non-urgent (low priority)
  • Keeps the UI responsive during expensive updates
  • Allows urgent updates (like typing) to interrupt transitions
  • Provides isPending to show loading states

When to Use:

  • Tab switching or filter changes that trigger expensive re-renders
  • Large list filtering or sorting operations
  • Any state update that can be delayed without hurting UX
  • When you want to prioritize user input over background updates

Code Example:

import { useTransition, useState } from 'react';

function TabComponent() {
  const [isPending, startTransition] = useTransition();
  const [tab, setTab] = useState('home');
  
  function handleTabChange(newTab) {
    // Non-urgent update - doesn't block UI
    startTransition(() => {
      setTab(newTab);
    });
  }
  
  return (
    <div>
      {isPending && <Spinner />}
      <button onClick={() => handleTabChange('home')}>
        Home
      </button>
    </div>
  );
}

Tab Navigation:

Current Tab: home

Generate Items:

Items generated: 0

Try clicking multiple times quickly - the UI stays responsive!

React 19 useOptimistic Hook

What is useOptimistic?

useOptimistic enables optimistic UI updates by showing immediate feedback before server confirmation. If the action fails, the UI automatically reverts to the previous state, ensuring consistency.

Key Features:

  • Updates UI instantly before server response
  • Automatically reverts if the action fails
  • Provides better user experience with immediate feedback
  • Perfect for actions like likes, comments, or updates

When to Use:

  • Actions that are likely to succeed (likes, follows, saves)
  • When you want instant UI feedback
  • Improving perceived performance
  • Reducing perceived latency in user interactions

Code Example:

import { useOptimistic, useState } from 'react';

function LikeButton() {
  const [likes, setLikes] = useState(0);
  
  const [optimisticLikes, addOptimistic] = useOptimistic(
    likes,
    (current, amount) => current + amount
  );
  
  async function handleLike() {
    addOptimistic(1); // Update UI immediately
    await likePost(); // Server call
    setLikes(prev => prev + 1); // Update real state
  }
  
  return <button onClick={handleLike}>Likes: {optimisticLikes}</button>;
}
0 likes

UI updates instantly, then syncs with server

React 19 useActionState Hook

What is useActionState?

useActionState (formerly useFormState) manages form state and server actions in one place. It handles async submission, error states, and pending states automatically.

Key Features:

  • Unified state management for form submissions and server actions
  • Automatic error and success state handling
  • Built-in pending state tracking
  • Works seamlessly with Next.js Server Actions

When to Use:

  • Forms with server-side validation and processing
  • When you need centralized error and state management
  • Combining with useFormStatus for form submission feedback
  • Replaces the older useFormState pattern

Code Example:

import { useActionState } from 'react';

async function submitForm(prevState, formData) {
  const email = formData.get('email');
  // Validation and server logic
  return { message: 'Success!', error: null };
}

function Form() {
  const [state, formAction, pending] = useActionState(
    submitForm,
    { message: null, error: null }
  );
  
  return (
    <form action={formAction}>
      <input name="email" disabled={pending} />
      {state.error && <p>{state.error}</p>}
      <button disabled={pending}>Submit</button>
    </form>
  );
}

Manages form state, validation, and server actions automatically

React 19 useFormStatus Hook

What is useFormStatus?

useFormStatus lets child components access the submission status of their nearest parent <form> element. This eliminates the need to pass pending state as props.

Key Features:

  • Automatically detects form submission status
  • Works only with Server Actions or form actions
  • No prop drilling needed for pending state
  • Must be used inside a form element

When to Use:

  • Submit buttons that need to show loading states
  • Form validation messages that depend on submission status
  • Avoiding prop drilling for form state
  • Better component composition with form elements

Code Example:

import { useFormStatus } from 'react-dom';

function SubmitButton() {
  const { pending } = useFormStatus();
  
  return (
    <button disabled={pending}>
      {pending ? 'Submitting...' : 'Submit'}
    </button>
  );
}

function Form() {
  return (
    <form action={serverAction}>
      <input name="email" />
      <SubmitButton /> {/* Knows form status automatically */}
    </form>
  );
}

The button above uses useFormStatus to automatically detect when the form is submitting, without needing to pass pending as a prop.

React 19 use Hook

What is use?

use is a React hook that lets you read the value of a resource like a promise or context. It can unwrap promises directly in render, automatically suspending until the promise resolves.

Key Features:

  • Unwraps promises and suspends until they resolve
  • Works with React's Suspense boundaries
  • Can also read context values
  • Simplifies async data handling in components

When to Use:

  • Reading promises from data fetching libraries
  • Accessing context values conditionally
  • Simpler async data handling compared to useEffect
  • Working with Suspense for better loading states

Code Example:

import { use, Suspense, useMemo } from 'react';

function UserProfile({ userPromise }) {
  // Unwraps promise, suspends until resolved
  const user = use(userPromise);
  
  return <div>{user.name}</div>;
}

function App() {
  // Memoize promise to prevent recreation
  const promise = useMemo(() => fetchUser(), []);
  
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <UserProfile userPromise={promise} />
    </Suspense>
  );
}

Handles promises directly in render, suspends automatically

Core Hooks

React useState Hook

What is useState?

useState is the most fundamental React hook for managing component state in functional components. It allows you to add stateful logic to components that were previously stateless.

Key Features:

  • Returns the current state value and a function to update it
  • Triggers re-renders when state changes
  • Can accept an initial value or a function that returns an initial value
  • State updates are batched for performance

When to Use:

  • Managing simple component state (counters, form inputs, toggles)
  • When state logic is straightforward and doesn't require complex updates
  • For state that's specific to a single component

Code Example:

import { useState } from 'react';

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

Counter: 0

useState is the most basic hook for managing component state. It returns the current state value and a function to update it.

React useEffect Hook

What is useEffect?

useEffect lets you perform side effects in functional components. It serves as a replacement for lifecycle methods like componentDidMount, componentDidUpdate, and componentWillUnmount.

Key Features:

  • Runs after the component renders to the screen
  • Can optionally clean up effects (return a cleanup function)
  • Controls when effects run using dependency arrays
  • Non-blocking - doesn't delay the browser from updating the screen

When to Use:

  • Data fetching from APIs or subscriptions
  • Setting up event listeners or timers
  • Manually updating the DOM
  • Any operation that should happen "outside" of the render cycle

Code Example:

import { useState, useEffect } from 'react';

function UserProfile() {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    // Fetch user data on mount
    fetchUser().then(setUser);
    
    // Cleanup function (runs on unmount)
    return () => {
      // Cancel any pending requests
    };
  }, []); // Empty deps = run once on mount
  
  return <div>{user?.name}</div>;
}

Counter: 0

Check the console to see effects running

Window Width: 0px

Resize the window to see the effect update

Component Status: Not Mounted

Common Use Cases:

  • Data fetching from APIs
  • Setting up subscriptions or event listeners
  • Manually changing the DOM
  • Cleanup when component unmounts

React useContext Hook

What is useContext?

useContext allows you to read and subscribe to context values without prop drilling. It lets you access values from a React Context anywhere in your component tree without passing props through intermediate components.

Key Features:

  • Eliminates the need to pass props through multiple component levels
  • Subscribes to context changes and re-renders when values update
  • Works with the Context API (createContext + Provider)
  • Can consume multiple contexts in a single component

When to Use:

  • Sharing data across many components in the tree
  • Theming, authentication, language preferences
  • When prop drilling becomes cumbersome
  • For data that doesn't need to be stored globally (use Redux/Zustand for that)

Code Example:

import { createContext, useContext } from 'react';

const ThemeContext = createContext('light');

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

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Button /> {/* Uses 'dark' theme */}
    </ThemeContext.Provider>
  );
}

Both components below share the same theme state through context:

Current Theme: light

Benefits of useContext:

  • Avoids prop drilling (passing props through multiple levels)
  • Shared state across component tree
  • Cleaner component APIs
  • Great for themes, authentication, language preferences

React useReducer Hook

What is useReducer?

useReducer is an alternative to useState for managing complex state logic. It follows the reducer pattern similar to Redux, where you dispatch actions to update state through a reducer function.

Key Features:

  • Centralized state updates through a reducer function
  • Predictable state transitions via action dispatches
  • Better for complex state with multiple sub-values
  • Easier to test and reason about state changes

When to Use:

  • Complex state logic with multiple related values
  • When next state depends on previous state
  • Managing state objects with nested properties
  • When you need more predictable state updates

Code Example:

import { 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;
  }
}

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

Count: 0

History:

0

When to use useReducer:

  • Complex state logic with multiple sub-values
  • When next state depends on previous state
  • Better testability and predictability
  • Easier to reason about state transitions

Performance Hooks

React useCallback Hook

What is useCallback?

useCallback returns a memoized version of a callback function that only changes if one of the dependencies has changed. It's useful for passing stable callbacks to child components, preventing unnecessary re-renders.

Key Features:

  • Returns the same function reference if dependencies haven't changed
  • Prevents child components from re-rendering unnecessarily
  • Essential when passing callbacks to memoized components (React.memo)
  • Similar to useMemo but specifically for functions

When to Use:

  • Passing callbacks to memoized child components
  • When function identity matters (useEffect dependencies, etc.)
  • Optimizing performance in lists with many items
  • Preventing infinite loops in useEffect dependencies

Code Example:

import { useCallback, memo } from 'react';

const TodoItem = memo(({ id, onRemove }) => {
  return <button onClick={() => onRemove(id)}>Remove</button>;
});

function TodoList() {
  const [todos, setTodos] = useState([]);
  
  // Memoized callback - same reference unless deps change
  const handleRemove = useCallback((id) => {
    setTodos(prev => prev.filter(t => t.id !== id));
  }, []);
  
  return todos.map(todo => (
    <TodoItem key={todo.id} onRemove={handleRemove} />
  ));
}

Counter (unrelated state): 0

💡 Increment the counter and check the console. TodoItem components won't re-render because handleRemove is memoized with useCallback.

Learn React 19 hooks
Build amazing apps
Share with the community

Key Benefits:

  • Prevents unnecessary re-renders of child components
  • Stable function reference across renders
  • Essential when passing callbacks to memoized components

React useMemo Hook

What is useMemo?

useMemo memoizes the result of an expensive computation, returning a cached value until one of its dependencies changes. It helps optimize performance by avoiding expensive calculations on every render.

Key Features:

  • Only recalculates when dependencies change
  • Returns cached value if dependencies haven't changed
  • Prevents unnecessary expensive computations
  • Can memoize objects and arrays to prevent reference changes

When to Use:

  • Expensive calculations that depend on specific values
  • Preventing unnecessary recalculations on every render
  • Optimizing performance when passing values as props
  • Memoizing derived state from complex computations

Code Example:

import { useMemo, useState } from 'react';

function ExpensiveComponent({ items, filter }) {
  // Only recalculates when items or filter changes
  const filteredItems = useMemo(() => {
    return items.filter(item => 
      item.name.includes(filter)
    );
  }, [items, filter]);
  
  return <div>{filteredItems.map(...)}</div>;
}

Counter (triggers re-render): 0

⚠️ Click this button and watch the console - "Without useMemo" recalculates every time, but "With useMemo" stays cached!

Multiplier: 1

Changing multiplier triggers recalculation for both (since multiplier is a dependency)

Results:

Without useMemo:

249,750,000

Executed 2 times - Check console to see it runs on every render!

With useMemo:

249,750,000

Executed 1 time - Only recalculates when multiplier changes!

💡 Test it: Increment the counter multiple times. Check your browser console - you'll see "Without useMemo" logs every time, but "With useMemo" only logs when multiplier changes!

When to use useMemo:

  • Expensive calculations that depend on specific values
  • Preventing unnecessary recalculations on every render
  • Optimizing performance for computed values
  • When the computation cost is significant

React useDeferredValue Hook

What is useDeferredValue?

useDeferredValue defers updating a value until after more urgent updates have completed. It's similar to debouncing but works with React's concurrent rendering, keeping the UI responsive during expensive operations.

Key Features:

  • Defers value updates until urgent updates complete
  • Keeps the UI responsive during expensive operations
  • Works automatically with React's concurrent rendering
  • Returns the previous value while update is pending
  • Perfect for search inputs and filtering large lists

When to Use:

  • Search/filter inputs with expensive filtering operations
  • Rendering large lists based on user input
  • Any expensive computation triggered by user input
  • When you want to prioritize input responsiveness over immediate results

Code Example:

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

function SearchProducts() {
  const [query, setQuery] = useState('');
  
  // Defer the query update until urgent updates complete
  const deferredQuery = useDeferredValue(query);
  
  // Expensive filtering uses deferred query
  const filteredProducts = useMemo(() => {
    return products.filter(p => 
      p.name.toLowerCase().includes(deferredQuery.toLowerCase())
    );
  }, [deferredQuery]);
  
  const isStale = query !== deferredQuery;
  
  return (
    <div>
      <input 
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Search..."
      />
      {isStale && <div>Searching...</div>}
      <ProductList products={filteredProducts} />
    </div>
  );
}

Interactive Search Demo:

Type in the search box to see how useDeferredValue keeps the input responsive while deferring expensive filtering operations. Notice how typing feels instant even though we're filtering through 5,000 products.

Renders: 0
Found 5000 products
Product 1
Electronics • $452
Product 2
Clothing • $883
Product 3
Food • $520
Product 4
Books • $1004
Product 5
Sports • $121
Product 6
Electronics • $758
Product 7
Clothing • $125
Product 8
Food • $592
Product 9
Books • $687
Product 10
Sports • $495
... and 4990 more

How It Works:

Without useDeferredValue:

  • Every keystroke triggers expensive filtering immediately
  • Input may feel laggy or unresponsive
  • UI blocks while filtering completes

With useDeferredValue:

  • Input updates immediately (urgent update)
  • Filtering is deferred until input settles
  • UI stays responsive during typing
  • Previous results shown while new ones are computed

Performance Tips:

  • Combine with useMemo to memoize expensive computations based on the deferred value
  • Use isPending pattern to show loading indicators when value is stale
  • Works best with concurrent rendering (React 18+) where urgent updates can interrupt non-urgent ones
  • Consider useTransition for marking entire state updates as non-urgent

React useLayoutEffect Hook

What is useLayoutEffect?

useLayoutEffect is identical to useEffect, but it runs synchronously after all DOM mutations and before the browser paints. This makes it useful for reading layout from the DOM and synchronously re-rendering.

Key Features:

  • Runs synchronously before the browser paints the screen
  • Blocks visual updates until it completes
  • Prevents visual flickering when DOM measurements are needed
  • Same API as useEffect (function, dependency array, cleanup)

When to Use:

  • DOM measurements that affect visual layout
  • Preventing visual flickering or "flash of unstyled content"
  • When you need to read layout and synchronously update before paint
  • Prefer useEffect for most cases (better performance)

Real-Life Use Cases:

  • Tooltip/Popover Positioning: Measuring an element's position and dynamically positioning a tooltip or popover to ensure it stays within the viewport without causing a visual flash.
  • Dynamic Height Calculations: Calculating and setting heights for animated accordions, collapsible sections, or expanding menus before the browser paints to prevent layout shift.
  • Scroll Position Restoration: Restoring scroll position after navigation or content changes, ensuring users don't see the content jump from one position to another.
  • Focus Management: Automatically focusing inputs or buttons after modal dialogs open, preventing the visible focus state from appearing before the element is ready.
  • Responsive Layout Adjustments: Measuring container widths to conditionally render different layouts (e.g., showing/hiding sidebar) before the first paint.

Code Example:

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

function Tooltip() {
  const [width, setWidth] = useState(0);
  const ref = useRef(null);
  
  // Runs before paint - prevents flickering
  useLayoutEffect(() => {
    if (ref.current) {
      setWidth(ref.current.offsetWidth);
    }
  }, []);
  
  return <div ref={ref} style={{ width }}>Tooltip</div>;
}

Interactive Demonstrations:

Hover over the button to see the tooltip position itself without flickering.

useLayoutEffect vs useEffect:

  • useLayoutEffect: Runs synchronously before browser paint
  • useEffect: Runs asynchronously after browser paint
  • Use useLayoutEffect when you need to prevent visual flickering
  • Use useLayoutEffect for DOM measurements that affect layout
  • Prefer useEffect for most cases (better performance)

Utility Hooks

React useRef Hook

What is useRef?

useRef returns a mutable ref object whose current property is initialized to the passed argument. The ref object persists across renders but changing it doesn't trigger a re-render.

Key Features:

  • Maintains the same ref object across re-renders
  • Changing .current doesn't cause re-renders
  • Can hold any mutable value (not just DOM elements)
  • Commonly used to access DOM elements directly

When to Use:

  • Accessing DOM elements directly (focus, scroll, measurements)
  • Storing mutable values that shouldn't trigger re-renders
  • Keeping track of previous values
  • Storing timers, intervals, or other imperative handles

Code Example:

import { useRef } from 'react';

function TextInput() {
  const inputRef = useRef(null);
  
  const handleFocus = () => {
    inputRef.current?.focus();
  };
  
  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleFocus}>Focus Input</button>
    </>
  );
}

Counter: 0

Previous count: N/A

Previous Value (stored in ref):

Previous count: N/A

Note: The ref value persists across renders but doesn't cause re-renders when updated directly.

Common useRef Use Cases:

  • Accessing DOM elements directly
  • Storing mutable values that don't trigger re-renders
  • Keeping previous values
  • Storing timers, intervals, or other imperatives

React useId Hook

What is useId?

useId generates a unique ID that is stable across server and client renders. It's particularly useful for accessibility attributes like htmlFor and id that need to match.

Key Features:

  • Generates unique, stable IDs across renders
  • SSR-safe (same ID on server and client)
  • Includes a colon (:) to ensure uniqueness within a component
  • Doesn't change between re-renders of the same component

When to Use:

  • Accessibility attributes (label/input, aria-labelledby, etc.)
  • Form field IDs that need to match labels
  • Server-side rendering where IDs must match between server and client
  • Avoiding ID conflicts in components rendered multiple times

Code Example:

import { useId } from 'react';

function FormField({ label }) {
  const id = useId(); // Stable ID across renders
  
  return (
    <>
      <label htmlFor={id}>{label}</label>
      <input id={id} type="text" />
    </>
  );
}

Counter: 0

IDs remain stable even when component re-renders

Generated IDs:

Main ID: _R_l4lubtb_
Name ID: _R_l4lubtbH1_
Email ID: _R_l4lubtbH2_

When to use useId:

  • Generate unique IDs for accessibility (label/input pairing)
  • Stable IDs across server and client renders (SSR)
  • Avoiding ID conflicts in lists or multiple instances
  • Creating unique keys that don't depend on data

Advanced Hooks

React useSyncExternalStore Hook

What is useSyncExternalStore?

useSyncExternalStore lets you subscribe to an external data source. It's designed to work with React's concurrent rendering features and is the recommended way to integrate external stores (like Redux, Zustand, or custom stores) with React.

Key Features:

  • Subscribe to external data sources safely
  • Works correctly with concurrent rendering and automatic batching
  • Prevents tearing (inconsistent UI state) during concurrent updates
  • Requires a subscribe function and a getSnapshot function

When to Use:

  • Integrating external state management libraries (Redux, Zustand, etc.)
  • Subscribing to browser APIs (localStorage, window size, network status)
  • Connecting to real-time data sources (WebSockets, Server-Sent Events)
  • Any external data source that needs to work with concurrent React

Code Example:

import { useSyncExternalStore } from 'react';

// Simple external store
class Store {
  private value = 0;
  private listeners = new Set();
  
  subscribe = (listener) => {
    this.listeners.add(listener);
    return () => this.listeners.delete(listener);
  };
  
  getSnapshot = () => this.value;
  
  increment = () => {
    this.value++;
    this.listeners.forEach(l => l());
  };
}

const store = new Store();

function Counter() {
  const value = useSyncExternalStore(
    store.subscribe,
    store.getSnapshot
  );
  
  return (
    <div>
      Count: {value}
      <button onClick={store.increment}>+</button>
    </div>
  );
}

External Store Demo:

This counter uses an external store that multiple components could subscribe to. The store value is synced using useSyncExternalStore.

0

From external store

Local state counter: 0 (updates separately to show difference)

localStorage Store Demo:

This demonstrates subscribing to localStorage, which syncs across browser tabs. Try opening this page in multiple tabs and updating the value.

Current value: React Hooks

This value persists across page refreshes and syncs across tabs!

useSyncExternalStore vs Other Approaches:

  • useSyncExternalStore: Safe for concurrent rendering, prevents tearing
  • useState + useEffect: Can cause tearing in concurrent mode, not recommended for external stores
  • Direct subscription: Works but doesn't integrate well with React's concurrent features
  • Library authors should use useSyncExternalStore for state management libraries

React useInsertionEffect Hook

What is useInsertionEffect?

useInsertionEffect is a hook primarily used by library authors to inject styles into the DOM before layout effects run. It runs synchronously before all DOM mutations and before useLayoutEffect. This prevents visual flickering when styles need to be applied before layout measurements.

Key Features:

  • Runs synchronously before all DOM mutations
  • Runs before useLayoutEffect (ensures styles exist before layout reads)
  • Primarily for CSS-in-JS library authors
  • Rarely needed in regular applications
  • Prevents FOUC (Flash of Unstyled Content)

When to Use:

  • Library authors: Building CSS-in-JS libraries (styled-components, emotion, etc.)
  • Dynamic style injection: When styles must exist before layout effects
  • Preventing flicker: When styles are computed dynamically
  • Most apps: You probably don't need this - use CSS classes or CSS-in-JS libraries

Execution Order:

  1. Component renders
  2. useInsertionEffect runs (inject styles)
  3. DOM mutations complete
  4. useLayoutEffect runs (can read computed styles)
  5. Browser paints
  6. useEffect runs (async, after paint)

Code Example:

import { useInsertionEffect, useRef } from 'react';

function StyledButton({ color }: { color: string }) {
  const idRef = useRef(`style-${Math.random()}`);
  const classNameRef = useRef(`btn-${idRef.current}`);
  
  // Inject styles before layout effects run
  useInsertionEffect(() => {
    const css = `
      .${classNameRef.current} {
        background-color: ${color};
        color: white;
        padding: 8px 16px;
      }
    `;
    
    // Inject into <head>
    const style = document.createElement('style');
    style.id = idRef.current;
    style.textContent = css;
    document.head.appendChild(style);
    
    // Cleanup
    return () => {
      document.getElementById(idRef.current)?.remove();
    };
  }, [color]);
  
  return <button className={classNameRef.current}>Click me</button>;
}

Dynamic Styled Component:

This button's styles are injected dynamically using useInsertionEffect. Change the color to see styles update before layout effects run.

Styles are injected synchronously before any layout measurements, preventing visual flicker.

Theme-Based Styling:

This component changes appearance based on theme. Styles are injected with useInsertionEffect to ensure they exist before the component renders.

Current theme: light

Styles injected with useInsertionEffect before layout

Execution Timing:

Click the button to see the execution order. Check your browser console to see that useInsertionEffect runs before useLayoutEffect.

⚠️ Important Notes:

  • For library authors: useInsertionEffect is essential for CSS-in-JS libraries
  • For app developers: You rarely need this - use CSS classes, CSS modules, or existing CSS-in-JS libraries instead
  • Only use when you need to inject styles that must exist before layout effects run
  • If you're building a CSS-in-JS library, this is the correct hook to use

useInsertionEffect vs Other Hooks:

useInsertionEffect: For injecting styles/scripts before layout
Runs: Before DOM mutations, before useLayoutEffect
useLayoutEffect: For DOM measurements and synchronous updates
Runs: After DOM mutations, before paint
useEffect: For side effects (API calls, subscriptions, etc.)
Runs: After paint (async)