async-react-patterns
Guides React 19 async UI state modeling with Actions, useActionState, useTransition, useOptimistic, use, Suspense, Activity, and useEffectEvent. Use when implementing or reviewing React async flows th
SKILL.mdskills/frameworks/react/async-react-patterns/SKILL.md
GitHubAsync React Patterns
Use this skill to avoid outdated async React patterns. Do not use it to fix async waterfalls; use vercel-react-best-practices for waterfall and performance work. This skill is about choosing the React 19 primitive that owns pending, optimistic, error, and streaming state instead of hand-rolling those states with scattered useState/useEffect logic.
Quick Start
- Classify the async work:
- read data: render under
Suspense, resolve withawaitin Server Components oruse(promise)in Client Components - mutate data: model pending/error/settled state as an Action, usually through
<form action>,useActionState, orstartTransition - preserve hidden UI: use
<Activity>for stateful hidden sections that should pre-render or resume cheaply - effect callback with latest props: use
useEffectEvent, not disabled dependencies
- read data: render under
- Pick the smallest primitive:
- form submit ->
<form action={action}>,useActionState,useFormStatus - non-form async update ->
useTransition - instant feedback during mutation/navigation ->
useOptimistic - stable promise from server/framework ->
use(promise)insideSuspense - RSC cache lifetime cleanup ->
cacheSignal
- form submit ->
- Verify UX states: pending indicator, optimistic rollback/settle behavior, error boundary or returned error state, accessibility (
aria-busy, disabled only when useful).
Patterns
Actions For Mutations
- Name async mutation callbacks with
Actionsuffix when they are intended to run in a transition or form action. - Prefer
<form action>/useActionStatefor form submissions; React handles pending state, error result state, and uncontrolled form reset. - Use
useFormStatusinside reusable form controls that need the nearest parent form pending state without prop drilling. - Use
useTransitionfor non-form work such as router/search-param changes, list filtering, tabs, or button-triggered async mutations. - Keep urgent input updates outside transitions; put expensive or async follow-up work inside the Action.
- If setting state after
awaitinsidestartTransition, re-check current React docs. React currently documents wrapping post-awaitstate updates in anotherstartTransition.
Optimistic UI
- Call
useOptimistic(value, reducer?)at component top level. - Call the optimistic setter only inside an Action, event handler, effect, or callback; never during render.
- Keep reducers pure. Use a reducer when multiple optimistic operations can overlap or when list updates need typed operations.
- Let parents own optimistic state when reusable children cannot derive the final display value. Children may expose
setValueAction,submitAction, oronChangeActionprops for parent-specific logic. - Always define the settle path: successful server/router value replaces optimistic value; rejected work rolls back via React or explicit error recovery.
Reading Async Data
- In Server Components, prefer
async/awaitwhen the component can block at that point. - To avoid blocking a Server Component shell, create/pass a stable promise to a Client Component and resolve it with
use(promise)underSuspense. - Do not create promises during Client Component render for
use; promises should come from a Suspense-aware framework/library or from the server. - Pair
use(promise)withSuspensefor loading and an Error Boundary orpromise.catchfallback for rejection. Do not wrapuseintry/catch. - Keep data passed from Server to Client serializable.
Effects And Background UI
- Do not default to
useEffect+fetchfor primary data reads. Prefer framework data loading, Server Components, cached promises, or Suspense-compatible libraries. - Use
useEffectEventonly for callbacks that are conceptually events fired from an Effect and need latest props/state without resubscribing the Effect. - Do not put Effect Events in dependency arrays; keep them declared in the same component or Hook as their Effect.
- Use
<Activity mode="hidden">for UI likely to be revisited or preloaded while hidden; remember hidden Activities unmount effects and defer hidden updates. - Use
cacheSignalonly in React Server Components to abort/cleanup work tied tocache()lifetime.
Review Checklist
- No manual
isLoading/error/datastate machine where an Action,useActionState,useFormStatus,Suspense, or Error Boundary should own the state. - Mutations expose pending, error, optimistic, and settle behavior in one obvious Action path.
- Reusable components accept Action props instead of owning application-specific async side effects.
- Suspense boundaries are close enough to avoid blanking the whole page but high enough to prevent boundary soup.
- Client promises used with
useare stable, cached, and rejected through Error Boundaries or fallback values. - Accessibility reflects async state without over-disabling controls.
- Experimental or canary APIs from articles are verified against current React docs before use.
Sources
- React 19 overview: https://react.dev/blog/2024/12/05/react-19
- React 19.2 overview: https://react.dev/blog/2025/10/01/react-19-2
useOptimistic: https://react.dev/reference/react/useOptimisticuseTransition: https://react.dev/reference/react/useTransitionuse: https://react.dev/reference/react/use- Async shift article: https://blog.logrocket.com/react-19-2-the-async-shift/
- Reusable Actions article: https://certificates.dev/blog/building-reusable-components-with-react-19-actions