useTransition vs useDeferredValue

JavaScriptVeeam

useTransition vs useDeferredValue

The main difference is what you apply them to.

  • useTransition wraps the state update logic. You use it when you're the one triggering the state change and want to mark that update as low-priority.
  • useDeferredValue wraps a value (like a state variable or prop). You use it to get a delayed version of a value that might be changing rapidly.

Both hooks help keep your app responsive by deferring non-urgent UI updates, but they do it from different angles.

useTransition

useTransition gives you a way to tell React that a specific state update is not urgent and can be interrupted by more important updates (like user input).

It returns an array with two items:

  • isPending: A boolean that tells you if the low-priority update is still in progress.
  • startTransition: A function that you wrap your low-priority state update in.

When to use it: Use useTransition when you have access to the state-setting function and want to show a pending/loading indicator while the transition is happening.10

Example:

Imagine a search input that filters a large list. Typing should feel instant, but updating the list might be slow.

import { useState, useTransition } from 'react';

function App() {
const [isPending, startTransition] = useTransition();
const [inputValue, setInputValue] = useState('');
const [searchQuery, setSearchQuery] = useState('');

const handleInputChange = (e) => {
// High-priority update: keeps the input field responsive
setInputValue(e.target.value);

// Low-priority update: wrapped in startTransition
startTransition(() => {
setSearchQuery(e.target.value);
});
};

return (
<div>
<input
onChange={handleInputChange}
value={inputValue}
/>
{isPending && <p>Loading list...</p>}
{/* <SlowList query={searchQuery} /> */}
</div>
);
}

In this code, updating the inputValue is immediate. The slower work of updating searchQuery (which would trigger the <SlowList> to re-render) is deferred, and isPending becomes true until it's done.

useDeferredValue

useDeferredValue accepts a value and returns a new copy of that value that will "lag behind" during urgent re-renders.

When to use it: This is useful when you don't control the state update logic directly. For instance, if the value is coming from a parent component as a prop or from a third-party hook. It's a more direct way to de-prioritize a part of your UI.

Example:

Let's refactor the previous example using useDeferredValue.

import { useState, useDeferredValue } from 'react';

function App() {
const [searchQuery, setSearchQuery] = useState('');
// The deferredQuery will lag behind the actual searchQuery
const deferredQuery = useDeferredValue(searchQuery);

const handleInputChange = (e) => {
setSearchQuery(e.target.value);
};

// You can manually check if the component is "stale"
const isStale = searchQuery !== deferredQuery;

return (
<div>
<input
onChange={handleInputChange}
value={searchQuery}
/>
{isStale && <p>Loading list...</p>}
{/* The list uses the deferred value, so it won't re-render on every keystroke */}
{/* <SlowList query={deferredQuery} /> */}
</div>
);
}

Here, the input updates the searchQuery state immediately. React re-renders, but deferredQuery still holds the previous value. React then schedules a low-priority re-render with the new value for deferredQuery. This keeps the input responsive.

00:00