How to Use useTransition Hook in React

In this article, you will learn how to implement the useTransition hook in React to improve application performance and user experience. We will cover what the hook does, when to use it, and provide a clear, step-by-step code example demonstrating how to defer slow state updates so your user interface remains highly responsive.

What is the useTransition Hook?

The useTransition hook is a React Hook introduced in React 18 that lets you update the state without blocking the user interface. It allows you to mark certain state updates as “transitions” (non-urgent updates), which React can interrupt if a more urgent event occurs, such as a user typing in an input field or clicking a button.

The hook returns an array with exactly two values: 1. isPending: A boolean flag that is true while the transition is processing. 2. startTransition: A function that lets you wrap a state update to mark it as a transition.

Basic Syntax

const [isPending, startTransition] = useTransition();

Step-by-Step Implementation

To implement useTransition, follow this practical example of a search input that filters a large list of items. Without useTransition, typing in the input would feel sluggish because the app tries to render the heavy list on every keystroke.

Step 1: Import the Hook

Import useTransition alongside useState from React.

import { useState, useTransition } from 'react';

Step 2: Initialize the Hook

Call the hook inside your functional component.

const [isPending, startTransition] = useTransition();
const [inputValue, setInputValue] = useState('');
const [list, setList] = useState([]);

Step 3: Wrap Non-Urgent Updates

Separate urgent updates from non-urgent ones. Updating the text input value is urgent and must happen immediately. Updating the filtered list is heavy and can be deferred using startTransition.

const handleChange = (e) => {
  // Urgent: Update the input value immediately
  setInputValue(e.target.value);

  // Non-Urgent: Wrap the heavy list generation in startTransition
  startTransition(() => {
    const heavyList = [];
    for (let i = 0; i < 20000; i++) {
      heavyList.push(`${e.target.value} - Item ${i}`);
    }
    setList(heavyList);
  });
};

Step 4: Use isPending to Show a Loading State

Utilize the isPending boolean to provide visual feedback to the user while the non-urgent state transition is being processed.

return (
  <div>
    <input type="text" value={inputValue} onChange={handleChange} />
    
    {isPending ? (
      <p>Loading results...</p>
    ) : (
      <ul>
        {list.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    )}
  </div>
);

Complete Code Example

Here is how the entire component looks when put together:

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

export default function SearchApp() {
  const [isPending, startTransition] = useTransition();
  const [inputValue, setInputValue] = useState('');
  const [list, setList] = useState([]);

  const handleChange = (e) => {
    setInputValue(e.target.value);

    startTransition(() => {
      const heavyList = [];
      for (let i = 0; i < 20000; i++) {
        heavyList.push(`${e.target.value} - Item ${i}`);
      }
      setList(heavyList);
    });
  };

  return (
    <div style={{ padding: '20px' }}>
      <h2>Search Filter</h2>
      <input 
        type="text" 
        value={inputValue} 
        onChange={handleChange} 
        placeholder="Type to filter..."
      />
      {isPending && <p style={{ color: 'blue' }}>Updating list...</p>}
      <ul>
        {list.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

Key Considerations