React

useTransition: non-urgent updates

useTransition marks an update as non-urgent: the input stays responsive and isPending tells you when the work is still running.

Key points

  • startTransition wraps the update that can wait; the urgent one stays outside and follows typing.
  • React interrupts a non-urgent render when new input arrives, so the field never drops characters.
  • If the list has twenty rows the transition adds complexity with no gain: measure before keeping it.

useTransition separates two state updates: the urgent one, which must follow typing, and the non-urgent one, which can wait. startTransition wraps the second and isPending tells you when it is still running.

What changes for the user

The controlled input updates at once, the filtered list arrives later. React interrupts the non-urgent render when a new key arrives, so the field never drops characters while the filter works.

The runnable case

import { useState, useTransition } from "react";

export function Ricerca({ righe }) {
  const [testo, setTesto] = useState("");
  const [filtro, setFiltro] = useState("");
  const [inCorso, startTransition] = useTransition();

  function cambia(e) {
    const valore = e.target.value;
    setTesto(valore);
    startTransition(() => setFiltro(valore));
  }

  const visibili = righe.filter((r) => r.includes(filtro));

  return (
    
      
      
        {visibili.map((r) => (
          {r}
        ))}
      
    
  );
}

In the code the input value is urgent and the filter is not. With ten thousand rows the difference is audible; with twenty it is not, and that is the moment to drop the transition and keep two plain setState calls.

Async actions

In React 19 startTransition accepts an async function: isPending stays true until the final await. A form can show its submit state without an extra boolean, and the result enters a single transition.

Live records in the catalogue

The search category collects the search fields in the catalogue, with a command menu that filters as you type; for the short wait there is the Kibo UI spinner, MIT licence.

The limitation

A transition does not make work fast: it moves the cost after the input and makes it interruptible. If the list is not memoised, React still re-renders it and the gain disappears; isPending is also a boolean, not a percentage.

Where to continue

What changed in the release is in React 19, what changes; waiting with a fallback is in Suspense: what it suspends; server-side streaming is in streaming and Suspense in Next.js. The React and Svelte comparison is in Svelte vs React.

Frequently asked questions

Are useTransition and useDeferredValue the same thing?

No. useTransition decides when an update you control starts; useDeferredValue starts from an existing value and needs no change to the setter.

Does a transition avoid the heavy work?

No, it makes it interruptible and moves it after the input. The non-urgent render still happens, so the total cost stays the same.

Why does isPending stay false?

Because the update finished within a frame, or because startTransition was called outside an event or an effect. With a small list that is the expected behaviour.

Can I use startTransition for the input value?

No, the input value must stay urgent. Put it in a transition and the field clears or drops characters while React postpones the render.

Does it work before React 19?

Yes, useTransition has existed since 18. The async function form and actions need 19.