Prioritize Updates with useTransition
Prioritize Updates with useTransition
A teacher needs the letters typed into a search box to appear promptly, even if updating a large result panel takes more work. The input and the report are connected, but they do not have the same urgency.
A transition marks supported state updates as nonurgent rendering work. React can prioritize more urgent interactions over that work. useTransition also exposes a pending flag; it does not move arbitrary JavaScript calculations to another thread.
Separate updates
Keep the controlled input’s value urgent.
Mark a transition
Schedule the result selection with startTransition.
Explain pending work
Distinguish scheduling from fetching and computation.
Follow the user’s interaction
Input
Typed text updates its own value promptly.
Transition
The result-query update is marked nonurgent.
Results
React renders the matching records.
Give the input and results separate roles
A controlled input must receive its current value from state and update that state during its change event. Do not place that value update inside a transition. A separate query can drive the result panel as nonurgent work. The temporary separation is deliberate: the text the teacher typed remains available while results catch up.
startTransition runs its callback immediately and marks the appropriate state updates. It is not a timer or a worker. React’s useTransition reference explains supported updates, pending state and controlled-input restrictions.
Urgent text and a transition query
The complete example starts with an empty query and all three names. Typing Maya updates input immediately, while setQuery is marked as transition work. After it completes, Matches: 1 and Maya remain. isPending selects a truthful result-update message.
Three names are too few to demonstrate a reliable perceptible delay, so Updating results may be brief or unseen. The example verifies the scheduling structure and final behavior, not a performance benchmark or a guaranteed duration. It performs no network request.
React · JavaScript with JSX
import React, { useState, useTransition } from 'react';
const students = ['Maya', 'Ravi', 'Asha'];
export default function App() {
const [input, setInput] = useState('');
const [query, setQuery] = useState('');
const [isPending, startTransition] = useTransition();
const visible = students.filter(name => name.toLowerCase().includes(query.toLowerCase()));
function change(event) {
const next = event.target.value;
setInput(next);
startTransition(() => setQuery(next));
}
return <main><h1>Student search</h1>
<label>Search names<input value={input} onChange={change} /></label>
<p role="status">{isPending ? 'Updating results' : 'Results ready'}</p>
<p>Matches: {visible.length}</p><ul>{visible.map(name => <li key={name}>{name}</li>)}</ul>
</main>;
}Expected screen and behavior
Initially: Student search, Search names, Results ready, Matches: 3 and all names.
After searching Maya: Matches: 1 with Maya.A transition can be interrupted and restarted
React · the input and query setters
import React, { useState, useTransition } from 'react';
const students = ['Maya', 'Ravi', 'Asha'];
export default function App() {
const [input, setInput] = useState('');
const [query, setQuery] = useState('');
const [isPending, startTransition] = useTransition();
const visible = students.filter(name => name.toLowerCase().includes(query.toLowerCase()));
function change(event) {
const next = event.target.value;
setInput(next);
startTransition(() => setQuery(next));
}
return <main><h1>Student search</h1>
<label>Search names<input value={input} onChange={change} /></label>
<p role="status">{isPending ? 'Updating results' : 'Results ready'}</p>
<p>Matches: {visible.length}</p><ul>{visible.map(name => <li key={name}>{name}</li>)}</ul>
</main>;
}React can delay or restart transition rendering when urgent work arrives. Keep component rendering pure so repeated calculation is safe. The eventual screen should describe the latest state, rather than depending on a render having run exactly once.
The filtering happens as part of rendering in this example. A long calculation written directly inside the transition callback would still run immediately and could block the thread before React can help with rendering priorities. For CPU work that cannot be split appropriately, consider a separate computation design such as a worker.
Asynchronous actions still need an ownership policy
React 19 supports asynchronous Actions within transitions, but starting a transition does not validate a response or stop older requests. Request errors, cancellations and outdated results still require an intentional data policy. A later response must not overwrite the teacher’s newer selection merely because both were inside transitions.
When scheduling state updates after await, the current documented limitation can require another startTransition around those updates. Learn that behavior from the current API reference rather than assuming the original callback’s timing automatically covers every later statement. For simple local filtering, keep this lesson’s synchronous transition small.
Worked practice: search without matching letter case
Begin with query ra and display its matching count. The complete answer initializes both the input and result query consistently. Typing ASHA should still produce one match because both the stored name and query are compared in lowercase.
Initializing only one state value would make the displayed field and initial results disagree. The two values have different scheduling roles, but their initial meaning should still agree. Once typing starts, the brief catch-up behavior is expected and is described by pending state.
React · JavaScript with JSX
import React, { useState, useTransition } from 'react';
const students = ['Maya', 'Ravi', 'Asha'];
export default function App() {
const [input, setInput] = useState('ra');
const [query, setQuery] = useState('ra');
const [isPending, startTransition] = useTransition();
const visible = students.filter(name => name.toLowerCase().includes(query.toLowerCase()));
function change(event) {
const next = event.target.value;
setInput(next);
startTransition(() => setQuery(next));
}
return <main><h1>Student search</h1>
<label>Search names<input value={input} onChange={change} /></label>
<p role="status">{isPending ? 'Updating results' : 'Results ready'}</p>
<p>Matches: {visible.length}</p><ul>{visible.map(name => <li key={name}>{name}</li>)}</ul>
</main>;
}Expected screen and behavior
Initially: Search names contains ra; Matches: 1 with Ravi.
ASHA also produces Matches: 1 with Asha.Transitions do not remove every source of delay
Do not use a transition to control the text input’s own value. Do not expect it to defer a long event-handler loop or to make requests finish faster. A pending label describes the chosen update, not proof that school data was saved.
Transitions can coordinate interface priority; debouncing limits how frequently work starts, and caching reuses previous data. Those are different techniques. Measure the actual interaction before deciding which one helps, and preserve failure and stale-response handling when the result comes from a server.
Check the urgent update
Apply the thread distinction
Prioritize the interface without hiding the data rules
Keep urgent input state separate from transition-driven result state, render purely and interpret pending feedback according to the work it represents. Transitions improve scheduling opportunities; they do not create workers, cache data or prevent stale requests.
Review performance measurement and request ownership before applying the same idea to a larger student search.
Reaching the recap and completing the quizzes are separate milestones.