React 19.2 Concurrency Patterns
Mobile patterns for transitions, Suspense, and route-level loading in Expo SDK 56.
Key takeaways
- Expo SDK 56 ships React 19.2.3; the production goal is keeping input responsive, isolating slow data, and marking the interactive point after splash work completes.
- Use
useTransitionso the input value updates immediately while heavier result rendering is deferred and a pending flag drives UI feedback. - Prefer many small Suspense boundaries (tab, sheet, action) over one root spinner so the navigation shell stays interactive.
- On native, treat Expo Router data loading as route-level cache orchestration, not server components solving mobile loading by themselves.
- Avoid starting network requests from render, blocking the app behind one root Suspense, or mixing direct React Navigation imports into an SDK 56 Router app.
React 19.2.3 on Mobile
Expo SDK 56 uses React 19.2.3. For mobile production, the important question is not the API list. It is whether rendering keeps input responsive, slow data loading is isolated, and the app marks its interactive point after splash work is truly complete.
useTransition
Use transitions when the input value should update immediately but the heavier result rendering can be deferred.
import { useState, useTransition } from 'react';
export function CustomerSearch({ customers }) {
const [query, setQuery] = useState('');
const [filtered, setFiltered] = useState(customers);
const [pending, startTransition] = useTransition();
function onChangeText(next: string) {
setQuery(next);
startTransition(() => {
setFiltered(searchCustomers(customers, next));
});
}
return <SearchList query={query} data={filtered} pending={pending} onChangeText={onChangeText} />;
}Suspense Boundaries
On mobile, one root spinner is usually worse than smaller boundaries. Keep the navigation shell interactive and split fallback UI by tabs, cards, sheets, and critical actions.
| Location | Recommended fallback |
|---|---|
| app root | splash or skeleton shell |
| tab content | list skeleton |
| modal/sheet | compact loading row |
| critical action | button-level pending state |
Expo Router and Data Loading
SDK 56 Expo Router strengthens web streaming SSR and data loading helpers. On native, treat this as route-level cache orchestration rather than expecting server components to solve mobile loading by themselves.
Operating rules:
- Bootstrap auth, feature flags, and remote config in the root layout and define the
markInteractive()point. - Wrap route transitions with slower data work in transitions.
- Keep a backend compatibility window when OTA updates change loader contracts.
- Give Expo Router web streaming SSR its own performance budget.
Anti-patterns
- Starting network requests directly from render paths.
- Blocking the entire app behind one root Suspense boundary.
- Hiding the splash screen and then waiting again for auth or remote config.
- Mixing direct React Navigation imports into an SDK 56 Expo Router app.
- Using
startTransitionas a substitute for optimistic rollback design.
Verification
- Measure input latency on slow 3G and lower-end Android devices.
- Check that Suspense fallback UI preserves accessibility labels and reading order.
- Use EAS Observe TTI P95 as the rollout gate, not only average values.