useTransition

All about useTransition in React


Improve performance by non-blocking the UI.

Good to know:

Use only when wrappring a setter function of an state.

import { useTransition } from 'react';
 
function TabContainer() {
  const [isPending, startTransition] = useTransition();
  ...
}

Everything inside the startTransition callback is marked as a low priority/Transitions. All Transitions can be interrupted in the middle of a re-render by other state updates/state setters (higher priority). Once all higher priority done, Transitions work are continue (restarts its rerender).

Use Cases
  • Setting 1000 items while the user interacts with a filter component. The user will be able to interact with the filter component since setting items are marked as Transition.
  • Changing tabs that renders a component with millions items while user clicks other tabs or components. By wrapping the setter of tabs with startTransition, the UI won't block.
export default function TabContainer() {
  const [tab, setTab] = useState('about');
  const [isPending, startTransition] = useTransition();
  
  // ⬇️
  function selectTab(nextTab) {
    startTransition(() => {
      setTab(nextTab);
    });
  }
 
  return 
  <>
      <TabButton onClick={() => selectTab('about')} >
        About
      </TabButton>
 
      <TabButton onClick={() => selectTab('posts')} >
        Posts (slow)
      </TabButton>
 
      {tab === 'about' && <AboutTab />}
      {tab === 'posts' && <PostsTab />}
  </>
}

In React 19, it will be available for async operations.

function onSubmit(data: Inputs) {
    if (!isLoaded) return
 
    startTransition(async () => {
      try {
        const result = await signIn.create({
          identifier: data.email,
          password: data.password,
        })
 
        if (result.status === "complete") {
          await setActive({ session: result.createdSessionId })
 
          router.push(`${window.location.origin}/`)
        } else {
          /*Investigate why the login hasn't completed */
          console.log(result)
        }
      } catch (error) {
        const unknownError = "Something went wrong, please try again."
 
        isClerkAPIResponseError(error)
          ? toast.error(error.errors[0]?.longMessage ?? unknownError)
          : toast.error(unknownError)
      }
    })
  }