What is useTransition Hook in React?
This article provides a clear guide on the useTransition
hook in React, explaining its core purpose, how it works, and how to
implement it to keep your application’s user interface responsive during
intensive state updates.
Understanding useTransition
Introduced in React 18, useTransition is a built-in
React Hook that lets you update the state without blocking the user
interface. It allows you to mark certain state updates as “transitions”
(non-urgent), which tells React they can be interrupted if a more urgent
user interaction occurs, such as typing or clicking.
By default, all state updates in React are treated as urgent. This
means if a state update causes a slow, heavy component to re-render, the
entire UI can freeze and become unresponsive until the render is
complete. useTransition solves this bottleneck.
Syntax
The useTransition hook does not take any arguments and
returns an array with exactly two elements:
const [isPending, startTransition] = useTransition();isPending: A boolean flag. It istruewhile the transition is actively running in the background, allowing you to display a loading spinner or visual feedback.startTransition: A function that lets you wrap a state update to mark it as a transition.
How to Use useTransition
Here is a practical example of how to implement
useTransition. Imagine a search input that filters a
massive list of items. Typing in the input should be instantaneous
(urgent), but rendering the filtered list can take time (non-urgent
transition).
import React, { useState, useTransition } from 'react';
function FilterList({ items }) {
const [query, setQuery] = useState('');
const [filteredItems, setFilteredItems] = useState(items);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
const value = e.target.value;
// 1. Update the input field immediately (Urgent)
setQuery(value);
// 2. Defer the heavy list filtering (Non-Urgent)
startTransition(() => {
const filtered = items.filter(item =>
item.toLowerCase().includes(value.toLowerCase())
);
setFilteredItems(filtered);
});
};
return (
<div>
<input type="text" value={query} onChange={handleChange} />
{isPending && <p>Updating list...</p>}
<ul style={{ opacity: isPending ? 0.5 : 1 }}>
{filteredItems.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}In this example, if the user continues to type while the list is
still filtering, React will abandon the outdated filter process and
prioritize the new keystrokes. The isPending state is used
to lower the opacity of the list, indicating to the user that the
content is updating.
When to Use useTransition
- CPU-heavy rendering: When a state update requires React to render a massive amount of DOM elements or perform complex calculations.
- Slow network responses: When navigating between tabs or pages where data is being fetched and you want to keep the current UI active instead of showing a blank screen.
When Not to Use It
- Controlled Inputs: Do not wrap the value of a text
input directly inside
startTransition. TextInput states must update synchronously to reflect keystrokes immediately. - Fast Operations: If your state update is fast and
does not cause lag, using
useTransitionadds unnecessary overhead.