Topics in this subject
React 4 min read Updated 6 Aug 2026

Concurrent Features

React 18 interruptible rendering with useTransition, useDeferredValue, and startTransition to keep the UI responsive.

🧑‍🏫 Sabse pehle — simple mein samjho#

Flipkart pe search box mein type karte ho toh tumhari typing kabhi atakti nahi — halaanki neeche hazaaron products ki heavy list update ho rahi hoti hai. Trick yeh hai ki typing ko urgent rakha jaata hai aur bhaari list update ko background mein daala jaata hai. React 18 ke concurrent features — useTransition aur useDeferredValue — yahi karte hain: heavy update ko peeche daal do taaki UI (typing, clicks) smooth rahe, atke na.

import { useState, useTransition } from "react";

const [isPending, startTransition] = useTransition();
const [query, setQuery] = useState("");

function onType(e) {
  setQuery(e.target.value);           // urgent — turant dikhe
  startTransition(() => {
    setResults(filterBigList(e.target.value)); // heavy — background mein
  });
}

Yaad rakho: useTransition/useDeferredValue = bhaari update ko background mein daalo, taaki typing fast rahe aur UI na atke.

The core idea#

React 18 introduced concurrent rendering: rendering work is now interruptible. React can start rendering an update, pause to handle a more urgent one (like typing), then resume — instead of blocking the main thread until a big render finishes. You opt in by marking some updates as non-urgent (transitions). 🎯

Two update priorities:

Priority Examples Behaviour
Urgent Typing, clicks, hovers Applied immediately, never interrupted
Transition Filtering a big list, tab switch, search results Rendered in the background; interruptible

useTransition#

Returns [isPending, startTransition]. Wrap the state update that triggers heavy work so it doesn't block the urgent input update.

function Search({ allItems }) {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState(allItems);
  const [isPending, startTransition] = useTransition();

  function onChange(e) {
    setQuery(e.target.value);              // urgent: input stays snappy
    startTransition(() => {                // non-urgent: heavy filter
      setResults(allItems.filter((i) => i.name.includes(e.target.value)));
    });
  }

  return (
    <>
      <input value={query} onChange={onChange} />
      {isPending && <Spinner />}
      <List items={results} />
    </>
  );
}

useDeferredValue#

Defers a value instead of wrapping an updater — useful when the expensive consumer is a child and you don't own the setState. React keeps showing the old value while re-rendering with the new one in the background.

function App({ text }) {
  const deferredText = useDeferredValue(text);   // lags behind during heavy renders
  return <SlowList text={deferredText} />;        // memoize SlowList to skip work
}

⚠️ useDeferredValue only helps if the heavy child is memoized (React.memo); otherwise it re-renders every time anyway.

startTransition (standalone)#

The non-Hook version for use outside components (no isPending), e.g. in a router or event handler.

import { startTransition } from "react";
startTransition(() => setPage("dashboard"));

When to use: keep an input responsive while a large derived list, chart, or tab re-renders. Do not wrap the state that drives a controlled input itself in a transition — the input value must be urgent, or typing feels laggy.

flowchart TD
  A["User types in input"] --> B["setQuery — URGENT"]
  B --> C["Input updates immediately"]
  A --> D["startTransition(setResults) — non-urgent"]
  D --> E["Heavy list renders in background"]
  E --> F{"New keystroke arrives?"}
  F -->|"Yes"| G["React interrupts, restarts with fresh input"]
  F -->|"No"| H["Commit list, isPending -> false"]

Interview Q&A#

Q1. What does "concurrent rendering" actually mean? Rendering is interruptible. React can begin a render, yield to handle higher-priority updates (like user input), and resume or discard the in-progress render. It's a capability, not a mode you turn on globally — features like transitions opt into it.

Q2. useTransition vs useDeferredValue? useTransition wraps the state updater you control and gives you isPending. useDeferredValue wraps a value you receive (e.g. a prop) when you can't wrap its setter. Both downgrade work to non-urgent; pick based on whether you own the update.

Q3. Why shouldn't you wrap a controlled input's own value update in a transition? The input value must reflect keystrokes instantly. Transitions are interruptible and may lag, making typing feel broken. Keep setQuery urgent; mark only the expensive derived update as a transition.

Q4. Does useDeferredValue make a slow component fast? No — it defers when the component receives the new value, letting the old UI stay interactive. The heavy child must be memoized so it can skip re-rendering with the stale value; otherwise there's no benefit.