A React page has a text input and a large list that re-renders slowly on every keystroke. Both useTransition and useDeferredValue can keep the input responsive — how do you decide which to use?
answer
- one marks an update, the other a value
- do you own the setter?
- one hands you a flag, one you derive
- the input stays urgent either way
- the consumer must be able to bail out
basics
~20 sUse useTransition when you own the state update and can wrap the setter, since it also gives you an isPending flag. Use useDeferredValue when the slow render is driven by a value you only receive, or when you want the lag localized inside the slow component.
solid answer
~50 sThey differ in what you have your hands on. `useTransition` marks an *update* as non-urgent, so it requires that you are the one calling the setter — you wrap `startTransition(() => setFilter(next))` — and in exchange it hands you `isPending` for free. `useDeferredValue` marks a *value* as allowed to lag, so it works even when you did not cause the change: a prop, a context value, or state owned by a parent you cannot edit. It gives you no flag, but you can derive one by comparing the current value with the deferred one. In the type-ahead case, either works, and both require the same thing to pay off: the input's own state stays urgent, and the expensive subtree must be able to bail out of the urgent render. Reach for the deferred value when the slow component sits far from where the state lives.
go deeper
Be able to state the split in one line: useTransition marks an update you trigger, useDeferredValue marks a value you receive. Mention that only the first gives you a ready-made pending flag.
Walk through the ownership test — do you control the setter — and note that the transition version needs two pieces of state while the deferred version derives one from the other.
Show the precondition both share: keep the input urgent and make sure the expensive subtree can bail out, then explain how you surface staleness in each case without blanking the screen.
Argue where the responsibility belongs: a component that defends itself with a deferred value needs nothing from callers, while a transition spreads a convention every call site must follow. Decide which cost your codebase should carry before standardizing.
## The two shapes Both APIs express "this work is not urgent", but they attach that label to different things. ```js // A: mark the update const [isPending, startTransition] = useTransition(); function onChange(e) { setQuery(e.target.value); // urgent: the input startTransition(() => setFilter(e.target.value)); // non-urgent: the list } // B: mark the value const deferredQuery = useDeferredValue(query); <List query={deferredQuery} /> ``` In A there are two pieces of state and the second is updated inside a transition. In B there is one piece of state and the expensive consumer reads a lagging copy of it. ## The decisive question: do you own the setter? `startTransition` can only mark updates you trigger. If the value that drives the slow render arrives as a prop, comes from context, or is set by code you do not control, you have no setter to wrap, and `useTransition` is simply unavailable to you at that site. `useDeferredValue` has no such requirement — it takes whatever value it is handed. That makes it the natural tool inside a reusable component that receives an expensive-to-render prop, because the component defends itself without demanding anything of its callers. When you *do* own the setter, both are on the table, and the tiebreaker is usually the pending affordance. ## The pending signal `useTransition` returns `isPending` maintained by React. `useDeferredValue` returns only the value, so you compute staleness yourself: ```js const isStale = query !== deferredQuery; ``` For primitives this comparison is exact and free. For richer cases the built-in flag is more convenient, and it is also the correct source when the pending state should cover an async action rather than just a render. ## What neither of them does Neither API makes a slow component fast. Both reorder work so that the urgent part — usually the keystroke landing in the input — commits first. That has two consequences worth saying out loud in an interview: - **The input's own state must stay urgent.** If you wrap the controlled input's setter in a transition, or feed the input a deferred value, the text itself lags behind the keys and typing feels broken. - **The expensive subtree must be able to skip the urgent render.** With `useDeferredValue`, that means the consumer is wrapped in `memo` or is a stable element passed as `children`; otherwise it re-renders anyway with the old value and you have merely added a second render. With `useTransition`, the split is structural: the expensive subtree reads a different piece of state, which simply did not change in the urgent render. ## Two states or one? The transition version implies two pieces of state that hold the same information at slightly different times. That duplication is real and has to be maintained — every code path that sets one must set the other. The deferred version keeps a single source of truth and derives the lagging copy, which is why it tends to read better for the type-ahead specifically. The transition version reads better for discrete actions — switching a heavy tab, applying a filter panel, committing a sort — where there is a clear moment with a clear pending affordance. ## Can you use both? Yes, and it is not a contradiction: they operate at different points. A page may start a transition for a heavy navigation and, separately, defer a value inside a chart component so the chart lags behind its own props. Combining them on the same value is redundant, though — marking the update non-urgent and then also deferring its consumption just adds renders. ## A quick decision procedure 1. Is the driving value something I set? If no → `useDeferredValue`. 2. Do I need a pending indicator that also covers an async step? If yes → `useTransition`. 3. Is the slow consumer far from the state, or is it a reusable component defending itself? → `useDeferredValue`. 4. Is this a discrete action with a clear "applying…" moment? → `useTransition`. And in every branch, confirm the same precondition: the urgent path — the input, the click target — is still urgent, and the expensive subtree can actually be skipped.
- Why is useDeferredValue often the better fit inside a reusable component?Because it needs nothing from the caller. The component receives a prop it cannot control and defers it locally, so the responsiveness fix lives with the code that is slow. A transition would require every caller to wrap its setter, which is a contract you cannot enforce and which breaks the moment someone updates the value from a new place.
- If you use the useTransition approach for a type-ahead, what duplication do you take on?Two pieces of state carrying the same information at different times: the urgent one bound to the input and the transition-marked one driving the list. Every path that sets one must set the other, including resets and programmatic changes, or they drift apart. `useDeferredValue` avoids that by deriving the lagging copy from a single source of truth.
- Does using either API mean you no longer need to memoize the expensive list?No — for `useDeferredValue` memoization is the precondition, since an unmemoized child re-renders with its parent and never sees the benefit of the old value. The `useTransition` split works without `memo` only because the expensive subtree reads different state that did not change in the urgent render; if it also reads the urgent state, you are back to memoizing.
saying these in an interview costs you the question
- Says one is simply the newer replacement for the other
- Wraps the controlled input's own setter in the transition
- Assumes useDeferredValue exposes a pending boolean
- Claims either one makes the slow render itself faster
- Thinks useTransition works on a value you receive as a prop