useTransition Hook
useTransition Hook — UI refresh without slowing down the UI
useTransition is a React 18 hook that allows React to differentiate between which updates are urgent and which are non-urgent. This way React can prioritize updates that immediately affect the user experience.
For example: a user types in a search input. React needs to simultaneously: 1️⃣ Update the input value (urgent) 2️⃣ Filter hundreds of data items in a list (non-urgent)
Without useTransition, the input might "freeze" or respond slowly. useTransition solves exactly this problem.
const [isPending, startTransition] = useTransition();
import React, { useState, useTransition } from 'react';
export default function SearchList() {
const [query, setQuery] = useState('');
const [filtered, setFiltered] = useState([]);
const [isPending, startTransition] = useTransition();
const items = Array.from({ length: 5000 }, (_, i) => `Element ${i + 1}`);
const handleChange = (e) => {
const value = e.target.value;
setQuery(value);
startTransition(() => {
const filteredItems = items.filter((item) =>
item.toLowerCase().includes(value.toLowerCase())
);
setFiltered(filteredItems);
});
};
return (
<div>
<h2>🔎 Filtering Example with useTransition</h2>
<input
type="text"
value={query}
onChange={handleChange}
placeholder="Search..."
/>
{isPending && <p>Loading...</p>}
<ul>
{filtered.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
Here React updates the input value immediately, while list filtering is performed within the transition scope without "freezing" the UI. If the filtering process takes a bit longer, React maintains the input update with invisible latency.
setQuery() — executes immediately as urgent updatestartTransition() — starts non-urgent update (filtering)isPending is true until filtering completesYou can use the `isPending` value to display a loader or skeleton.
{isPending ? (
<p>Loading new data...</p>
) : (
<List data={filtered} />
)}
This way React can transition between data more smoothly without visible jank.
This hook is also excellent for pagination or tab-switching, when new page content needs to load without brief UI freeze.
import React, { useState, useTransition } from 'react';
export default function PaginationExample() {
const [page, setPage] = useState(1);
const [isPending, startTransition] = useTransition();
const handlePageChange = (newPage) => {
startTransition(() => {
setPage(newPage);
});
};
return (
<div>
<h3>Page {page}</h3>
<button onClick={() => handlePageChange(page - 1)} disabled={page === 1}>
Previous
</button>
<button onClick={() => handlePageChange(page + 1)}>
Next
</button>
{isPending && <p>Loading next page...</p>}
</div>
);
}
Here the UI never "freezes" when switching between pages. React first displays the action result, then starts the non-urgent update.
React implements Concurrent Rendering — allowing two rendering processes simultaneously: one priority (urgent), the other in the background (deferred). It "suspends" non-urgent rendering if new urgent actions occur.
This is one of React's major innovations that allows writing responsive UI — without additional throttling or debounce logic.
Write a component that:
Try the difference without useTransition and see how the input responds. The result is obvious 💨.
[isPending, startTransition].