CONCEPT Cited by 1 source
Render-as-you-fetch¶
Render-as-you-fetch is the React-native data-loading pattern
that concurrent rendering is designed for. A component renders, its
data-fetching hook initiates the fetch, the hook throws a Promise
until the data arrives, <Suspense> catches the throw and shows the
fallback, and when the Promise resolves React resumes rendering
that subtree.
The key property: rendering, not a prior effect, triggers the fetch. Contrast with:
- Fetch-on-render (React pre-18):
useEffect(() => fetch(...))inside the component — fetch starts after the render commits, forcing a second re-render. - Fetch-then-render: fetch in the parent, pass data in as props — decouples fetching from rendering but requires the parent to know what its children need.
Render-as-you-fetch fuses the declarative component tree with the data dependency graph: each component knows what it needs, fetches it as part of rendering, and suspends if not ready.
Canonical shape (theoretical)¶
// Hypothetical Suspense-compatible data hook.
function ProductDetail({ id }) {
const product = useProduct(id); // throws a Promise if not ready
return <div>{product.name}</div>;
}
<Suspense fallback={<Spinner/>}>
<ProductDetail id="123"/>
</Suspense>
Why Zalando rejected it for Rendering Engine¶
Zalando's Rendering Engine evaluated this pattern and did a PoC, then turned around. The [[sources/2023-07-10-zalando-rendering-engine-tales-road-to-concurrent-react|2023-07 post]] documents four concrete blockers:
-
SuspenseListis experimental and limited. RE needs ordered streaming and hydration — Renderers appear in a specific order on the page. The current idiomatic way to constrain Suspense ordering isSuspenseListwhich is "still to be experimental, with some limitations". -
useTransitiondoesn't consider nested Suspense boundaries. RE composes deeply-nested Renderers (page → collection → product card). The known issue is thatuseTransitiondoesn't treat nested Suspense as first-class, producing "bad UX in some scenarios". -
Hook-initiated fetches couple fetch timing to render order. "By utilizing hooks to initiate requests or other async operations, the timing of fetch operations becomes coupled with the order of rendering, which may not be optimal for performance." A top-down render walk serialises fetches that could otherwise run in parallel. The framework-side solution would be to resolve data before rendering, write it into a state store, and read from the store in render — which is exactly the inverse of this pattern.
-
The streaming/caching layer for React data is not final. Progressive hydration and streaming need the data that drove server rendering to be available to the client without a second fetch round-trip. React's supporting feature (facebook/react#25502) was not finalised at PoC time.
Zalando's chosen alternative is to keep React as the renderer but move data resolution out to an Application-State layer outside React that writes data and has a "Connector hook" on the read side. The Connector hook is compatible with Suspense — it returns data or throws a Promise — but the data resolution runs in a plain state machine, not in a render tree.
When it is a good fit¶
- Simple per-component data dependencies — one component needs one query; no ordering constraints across siblings.
- No deep nested Suspense scheduling concerns —
useTransitionpitfalls don't apply. - Data-cache layer available (React Query, SWR, a framework that supplies one) so the client doesn't re-fetch what was server-rendered.
- Small or simple component tree — the serialisation-through- render-walk cost doesn't dominate.
Next.js App Router + React Server Components is the canonical framework-supplied render-as-you-fetch stack; both conceal the data-cache plumbing from the application and thus side-step the streaming/caching-layer concern.
When to reach for something else¶
- Rendering order is business-logic-driven (Zalando RE — personalisation-chosen Entity tree).
- Fetches across siblings must be parallelised without a prior waterfall.
- The data-cache-rehydration mechanism is under your control and you want to own it.
- You have a central state store (Redux-family) that already resolves data for other reasons and pairing it with React's Suspense read path is cheaper than migrating to hooks.
In these cases, patterns/application-state-layer-outside-react tends to be a cleaner architectural fit.
Seen in¶
- sources/2023-07-10-zalando-rendering-engine-tales-road-to-concurrent-react — canonical wiki instance of a deliberate rejection of render-as-you-fetch. The four concrete blockers are named with links to the upstream React issues/PRs. The chosen alternative (Application-State layer + Connector hook) is a full replacement at the data-resolution layer, not a tweak.
Related¶
- concepts/concurrent-rendering-react — the React feature set render-as-you-fetch pairs with.
- patterns/suspense-boundary — the render-as-you-fetch read boundary.
- patterns/application-state-layer-outside-react — the named alternative.
- concepts/react-hydration — the client-side contract.
- systems/react — the runtime.