Skip to content

React 18: Mastering useTransition for Smooth UI

Posted on:February 8, 2026 at 07:19 PM

Hey there, code chefs! 👨‍🍳

Today we’re cooking up something special from the React 18 menu. You know that feeling when you’re typing in a search box and the whole app freezes for a second? It’s like trying to chop vegetables with a dull knife—frustrating and dangerous!

That happens because React, by default, treats all updates as urgent. It tries to update the input value and filter that massive list of data at the exact same time.

Enter useTransition. Ideally, this hook tells React: “Hey, update the input immediately, but take your time with the list filtering.”

Let’s sharpen our knives and dive in! 🔪

The Problem: Blocking Updates 🚫

Imagine you have a list of 10,000 items. Every time you type a character, React has to filter that list and re-render it.

Without useTransition, the user types ‘a’, and the screen freezes until the list is filtered. The user types ‘b’, freeze again. It feels sluggish.

The Solution: useTransition to the Rescue 🦸‍♂️

useTransition gives you two things:

  1. isPending: A boolean that tells you if the transition is still happening.
  2. startTransition: A function that lets you mark state updates as “non-urgent”.

Here is how you use it:

import { useState, useTransition } from "react";

// Let's pretend this is a heavy operation
const filterItems = (query) => {
  const items = Array.from({ length: 10000 }, (_, i) => `Item ${i + 1}`);
  return items.filter(item => item.includes(query));
};

const SearchComponent = () => {
  const [query, setQuery] = useState("");
  const [list, setList] = useState([]);
  const [isPending, startTransition] = useTransition();

  const handleChange = (e) => {
    const value = e.target.value;

    // 1. Urgent update: Update the input field immediately
    setQuery(value);

    // 2. Non-urgent update: Filter the list when you have time
    startTransition(() => {
      const filtered = filterItems(value);
      setList(filtered);
    });
  };

  return (
    <div className="p-4">
      <input
        type="text"
        value={query}
        onChange={handleChange}
        className="border p-2 rounded"
        placeholder="Search..."
      />

      {isPending && <p className="text-gray-500">Updating list...</p>}

      <ul className="mt-4">
        {list.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
};

export default SearchComponent;

How It Works ⚙️

  1. Urgent Update: setQuery(value) happens instantly. The user sees what they typed right away. No lag.
  2. Transition: Inside startTransition, we calculate the filtered list and set the list state. React knows this can be interrupted.
  3. Feedback: While React is working on the list in the background, isPending becomes true. We can show a spinner or a message like “Updating list…“.

If the user types another character before the list is done filtering, React will throw away the old work and start fresh with the new character. The UI stays responsive!

When to Use It? 🤔

Don’t wrap everything in startTransition. Use it when:

Don’t use it for:

Conclusion

useTransition is like having a sous-chef. You handle the immediate orders (typing), while they prep the heavy ingredients (filtering) in the background. It keeps your kitchen (app) running smoothly even during the dinner rush.

Give it a try in your next heavy-duty component!

Happy Coding! 🚀