A sibling loading.tsx auto-wraps the page tree in a Suspense boundary. Async pages stream the fallback first, then emit the real content via the framework's out-of-order template+swap protocol — same machinery the explicit <Suspense> demos use.
Tip
The <code>loading.tsx</code> fallback is sent in the first chunk before the page even begins resolving — so TTFB stays short even for slow loaders. Unlike a client-side spinner, the fallback is server-rendered and visible with JavaScript off.
Resolved
Page resolved at 2026-09-06T04:04:54.689Z. Reload — you should see the Loading… fallback flash for ~1.5s in streaming mode before this content swaps in.
(View source to see both the fallback and the real content in the body, with a __bextSuspense.swap(N) inline script wiring them together. The fallback ships immediately; the real content arrives ~1.5s later as a separate chunk.)
src/app/examples/loading-demo/loading.tsxTSX
// src/app/examples/loading-demo/loading.tsx
export default function Loading() {
return <h1>Loading…</h1>;
}
// src/app/examples/loading-demo/page.tsx
// opt out of the site default ISR so it streams every request
export const dynamic = "force-dynamic";
export default async function Page() {
await new Promise((r) => setTimeout(r, 1500));
return (
<div>
<h1>loading.tsx</h1>
<p>Resolved at {new Date().toISOString()}</p>
</div>
);
}