React Concurrent
React Concurrent Features for responsive UIs.
by claude-dev-suite · MIT
The skill
React Concurrent Features for responsive UIs. Covers useTransition, useDeferredValue, Suspense, startTransition, concurrent rendering patterns, and priority-based updates. USE WHEN: user mentions "useTransition", "useDeferredValue", "startTransition", "concurrent rendering", "non-urgent updates", asks about "keeping UI responsive", "priority updates in React" DO NOT USE FOR: React 17 and earlier (concurrent mode not available), Simple applications without heavy rendering, Non-React frameworks
React Concurrent Features
Full Reference: See advanced.md for advanced concurrent patterns including priority-based updates, optimistic updates with transitions, router integration, form actions, and debugging techniques.
Deep Knowledge: Use
mcp__documentation__fetch_docswith technology:react, topic:concurrentfor comprehensive documentation.
When NOT to Use This Skill
Skip this skill when:
- Using React 17 or earlier (not available)
- Building simple apps with fast renders
- All state updates are already fast (< 16ms)
- Working with non-React frameworks
- Server Components handle the work (Next.js App Router)
Overview
Concurrent React allows interrupting renders to keep the UI responsive:
Traditional Rendering:
[Update starts]────────────────────────[Render complete]
UI blocked!
Concurrent Rendering:
[Update starts]──[pause]──[higher priority]──[resume]──[complete]
UI remains responsive!
useTransition
Mark state updates as non-urgent (can be interrupted):
import { useState, useTransition } from 'react';
function TabContainer() {
const [tab, setTab] = useState('home');
const [isPending, startTransition] = useTransition();
function selectTab(nextTab: string) {
startTransition(() => {
setTab(nextTab);
});
}
return (
<div>
<nav>
{['home', 'about', 'contact'].map((t) => (
<button key={t} onClick={() => selectTab(t)}>
{t}
</button>
))}
</nav>
<div className={isPending ? 'opacity-50' : ''}>
{tab === 'home' && <Home />}
{tab === 'about' && <About />}
{tab === 'contact' && <Contact />}
</div>
</div>
);
}
useDeferredValue
Defer updating a value to keep UI responsive:
import { useState, useDeferredValue, memo } from 'react';
function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
const isStale = query !== deferredQuery;
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
<div className={isStale ? 'opacity-50' : ''}>
<SearchResults query={deferredQuery} />
</div>
</div>
);
}
// Memoize to actually benefit from deferred value
const SearchResults = memo(function SearchResults({ query }: { query: string }) {
const results = useMemo(() => {
return items.filter(item =>
item.name.toLowerCase().includes(query.toLowerCase())
);
}, [query]);
return <ul>{results.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
});
startTransition (without Hook)
For use outside components or when you don't need isPending:
import { startTransition } from 'react';
function handleClick() {
startTransition(() => {
setPage('/heavy-page');
});
}
Suspense with Transitions
function App() {
const [tab, setTab] = useState('home');
const [isPending, startTransition] = useTransition();
return (
<div>
<nav>
<button onClick={() => startTransition(() => setTab('posts'))}>
Posts
</button>
</nav>
<Suspense fallback={<TabSkeleton />}>
<div className={isPending ? 'opacity-50' : ''}>
{tab === 'posts' && <Posts />}
</div>
</Suspense>
</div>
);
}
useTransition vs useDeferredValue
| Feature | useTransition | useDeferredValue |
|---|---|---|
| Purpose | Wrap state updates | Defer a value |
| Usage | When you control the update | When you receive a value |
| Returns | [isPending, startTransition] |
Deferred value |
| Use case | Button clicks, form submits | Props from parent |
Anti-Patterns
| Anti-Pattern | Why It's Bad | Correct Approach |
|---|---|---|
| Wrapping every state update | Adds unnecessary complexity | Only for heavy updates |
| Not memoizing child components | No benefit from deferred value | Use memo() |
| Using for fast operations | Performance overhead | Reserve for slow renders (> 50ms) |
| Ignoring isPending state | User sees no feedback | Show loading indicator |
| Multiple transitions unnecessarily | Confusing behavior | Batch related updates |
Quick Troubleshooting
| Issue | Likely Cause | Solution |
|---|---|---|
| No performance improvement | Child not memoized | Add memo() to component |
| Still blocking UI | Synchronous heavy computation | Move to Web Worker |
| isPending always false | Update completes too fast | No transition needed |
| Stale UI shown too long | No loading indicator | Check and display isPending |
| Transition not working | Using in React < 18 | Upgrade to React 18+ |
Best Practices
- ✅ Use for user-initiated heavy updates
- ✅ Combine with
memo()for list components - ✅ Show visual feedback during pending state
- ✅ Use for route transitions
- ✅ Prefer
useDeferredValuefor received props - ❌ Don't wrap every state update
- ❌ Don't use for fast operations
- ❌ Don't forget to memoize child components
Reference Documentation
Deep Knowledge: Use
mcp__documentation__fetch_docswith technology:react, topic:concurrentfor comprehensive documentation.
Use this skill
Connect your agent to the Skill Store MCP server and ask for it by ref: claude-dev-suite/react-concurrent-17841319105.
Details
- Publisher
- claude-dev-suite
- Category
- Software Development › Languages & frameworks
- License
- MIT
- Updated
- 2026-10-19
References
More from claude-dev-suite
- Axe Core axe-core - accessibility testing engine for automated WCAG compliance checks.
- Wcag WCAG 2.2 Web Content Accessibility Guidelines.
- Anthropic Python Anthropic Python SDK for Claude API integration.
- Decision Model Calibration Turning a model's class probabilities into numbers you can set a threshold on: building the label…