Mahamudul Hasan Rubel
HomeBlogCoursesAboutProjectsSkillsExperiencePhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • Blog
  • Courses
  • About
  • Projects
  • Skills
  • Experience
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

Subscribe to the newsletter

Get new articles and course lessons delivered to your inbox. No spam, unsubscribe anytime.

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
Lesson 11 of the Advanced React: Performance, Architecture & Patterns course
ReactJune 27, 20264 min read

Non-blocking UI with useTransition in React

Master useTransition to keep your React UI responsive. Learn how to mark state updates as non-urgent to prioritize user input during heavy rendering.

ReactPerformanceConcurrent ModeUI ResponsivenessHooksjavascriptfrontend

Previously in this course, we explored Introduction to Concurrent React: Time-Slicing and Performance, which established how React's concurrent engine can pause and resume rendering work. In this lesson, we build on those fundamentals to exert explicit control over that process using useTransition.

When building complex dashboards or data-heavy interfaces, a single user action—like typing in a search bar—often triggers a massive re-render of a list or chart. If that render takes 200ms, the input field feels "stuck." useTransition is your primary tool to tell React, "This specific update is less important than the user typing; let the input stay responsive while you process the heavy update in the background."

Understanding Transitions from First Principles

In standard React, all state updates are considered "urgent." When you call setState, React enters a synchronous render phase to update the DOM. If the component tree is deep or the data transformation is complex, the main thread remains blocked until the work completes.

A transition is a specific category of state update that React treats as non-urgent. By wrapping a state setter in startTransition, you instruct React to:

  1. Interrupt: If a more urgent update (like a keystroke) comes in, pause the ongoing transition work.
  2. Defer: Keep the current UI interactive while the "transitioning" state is computed in the background.
  3. Commit: Once the background work is finished, apply the changes to the DOM.

Worked Example: Filtering Large Lists

Imagine a dashboard displaying a list of 5,000 items. Filtering this list on every keystroke usually creates noticeable input lag. Let's optimize this using useTransition.

TSX
import { useState, useTransition } from CE9178">'react';

function LargeListFilter({ items }) {
  const [query, setQuery] = useState(CE9178">'');
  const [filteredItems, setFilteredItems] = useState(items);
  const [isPending, startTransition] = useTransition();

  const handleInputChange = (e) => {
    const value = e.target.value;
    
    // 1. Urgent: Update the input field immediately
    setQuery(value);

    // 2. Non-urgent: Filter the list in the background
    startTransition(() => {
      const nextFiltered = items.filter(item => 
        item.name.toLowerCase().includes(value.toLowerCase())
      );
      setFilteredItems(nextFiltered);
    });
  };

  return (
    <div>
      <input 
        value={query} 
        onChange={handleInputChange} 
        placeholder="Filter items..." 
      />
      {isPending && <p>Updating list...</p>}
      <ul style={{ opacity: isPending ? 0.5 : 1 }}>
        {filteredItems.map(item => <li key={item.id}>{item.name}</li>)}
      </ul>
    </div>
  );
}

In this example, the setQuery call happens immediately, ensuring the input value stays in sync with the user's keystrokes. The setFilteredItems call is wrapped in startTransition, allowing React to prioritize the input update and process the list filter as a low-priority task. The isPending boolean provides a hook to give visual feedback, such as dimming the list or showing a spinner.

Managing Pending States

While isPending is useful, rely on it sparingly. Over-using loading indicators can lead to "UI flickering" if the transition finishes too quickly.

FeatureUrgent State (setState)Transition State (startTransition)
PriorityHigh (Immediate)Low (Background)
User InputBlocked until finishInterrupted by input
Use CaseTyping, Toggling, SelectingFiltering, Sorting, Data Views

Hands-on Exercise

Refactor an existing search component in your project:

  1. Introduce useTransition to your search input handler.
  2. Observe the render count in the React DevTools Profiler (as taught in Profiling with React DevTools: Identifying Performance Bottlenecks).
  3. Add a visual indicator (like a CSS class or isPending flag) to signify that the list is stale while the transition is running.
  4. Compare the "Interaction to Next Paint" (INP) score before and after this change, as discussed in INP Optimization: Architecting Non-Blocking DOM Updates.

Common Pitfalls

  • Wrapping Everything: Don't wrap every setState in startTransition. If you wrap something that needs to be immediate (like a checkbox toggle or a button click), the UI will feel sluggish and unresponsive.
  • Missing Dependencies: Since startTransition is a function, ensure that any state derived inside it is correctly handled. If you use useMemo for the heavy calculation inside the transition, the state update won't actually be deferred until the calculation is done. Always perform the state-setting action inside the transition callback.
  • Async/Await: You cannot use await inside the startTransition callback. Transitions are for synchronous state updates. If you need to handle asynchronous data fetching, look toward useDeferredValue or Suspense, which we will cover in the next few lessons.

Recap

useTransition is the cornerstone of keeping your React application responsive during heavy data operations. By distinguishing between urgent user interactions and non-urgent data processing, you create a fluid experience that feels significantly faster to the end user. Remember: only defer updates that are visually heavy or computationally expensive.

Up next: We will explore how to synchronize deferred UI states using useDeferredValue.

Previous lessonIntroduction to Concurrent ReactNext lesson Handling Deferred Data with useDeferredValue
Back to Blog

Similar Posts

ReactJune 27, 20264 min read

Advanced Context Composition: High-Performance State Selectors

Master Context Selector hooks to prevent unnecessary re-renders. Learn how to implement granular state subscriptions and optimize your React architecture today.

Read more
ReactJune 27, 20264 min read

Optimizing Context Providers: Scaling React Performance

Learn to prevent tree-wide re-renders in your React application by optimizing Context Providers through value memoization, state splitting, and selective hooks.

Part of the course

Advanced React: Performance, Architecture & Patterns

advanced · Lesson 11 of 47

  1. 1

    Deep Dive into the Reconciliation Algorithm

    4 min
  2. 2

    Profiling with React DevTools

    3 min
  3. 3

    Establishing Performance Budgets

    3 min
Read more
ReactJune 27, 20264 min read

Mastering useCallback and useMemo for React Performance

Stop unnecessary re-renders by mastering useCallback and useMemo. Learn to stabilize references and optimize dependency arrays to keep your React app fast.

Read more
  • 4

    Strategic use of React.memo

    3 min
  • 5

    Mastering useCallback and useMemo

    4 min
  • 6

    State Colocation Strategies

    4 min
  • 7

    Optimizing Context Providers

    4 min
  • 8

    Advanced Context Composition

    4 min
  • 9

    Eliminating Prop Drilling

    4 min
  • 10

    Introduction to Concurrent React

    4 min
  • 11

    Non-blocking UI with useTransition

    4 min
  • 12

    Handling Deferred Data with useDeferredValue

    3 min
  • 13

    Mastering Suspense for Data Fetching

    4 min
  • 14

    Streaming Server-Side Rendering

    3 min
  • 15

    Designing Compound Components

    3 min
  • 16

    The Render Props Pattern

    4 min
  • 17

    Implementing Control Props

    4 min
  • 18

    Headless UI Architectures

    3 min
  • 19

    Modular Directory Structures

    3 min
  • 20

    Refactoring Monolithic Components

    3 min
  • 21

    Optimistic UI Updates

    3 min
  • 22

    Advanced Cache Invalidation

    4 min
  • 23

    Handling Race Conditions

    4 min
  • 24

    Server-Client State Synchronization

    3 min
  • 25

    Route-level Code Splitting

    4 min
  • 26

    Offloading Tasks with Web Workers

    3 min
  • 27

    Advanced Error Boundaries

    3 min
  • 28

    Monitoring Production Performance

    4 min
  • 29

    Final Project Audit & Optimization

    Coming soon
  • 30

    Advanced Hook Patterns

    Coming soon
  • 31

    Managing Global State with Zustand/Redux

    Coming soon
  • 32

    Testing Performance-Critical Components

    Coming soon
  • 33

    Static Site Generation (SSG) Patterns

    Coming soon
  • 34

    Internationalization (i18n) Architecture

    Coming soon
  • 35

    Accessibility (a11y) in Advanced Components

    Coming soon
  • 36

    Managing Third-Party Integrations

    Coming soon
  • 37

    Advanced Form Handling

    Coming soon
  • 38

    Using Portals for UI Overlays

    Coming soon
  • 39

    Implementing Virtualized Lists

    Coming soon
  • 40

    Building Design System Primitives

    Coming soon
  • 41

    Managing Large-Scale Data Fetching

    Coming soon
  • 42

    Micro-Frontends with React

    Coming soon
  • 43

    Security Best Practices in React

    Coming soon
  • 44

    Advanced Ref Usage

    Coming soon
  • 45

    Memoization Pitfalls

    Coming soon
  • 46

    Mastering React Patterns for Scalability

    Coming soon
  • 47

    Advanced TypeScript with React

    Coming soon
  • View full course