Next.js: The React Framework (App Router)Entry 7 of 16

Loading UI and Streaming

loading.tsx shows instant fallback UI while a route's data loads. <Suspense> does the same for any part of a page, and the server streams each part as it's ready.

react.dev<Suspense>
nextjs.orgloading.js

Notes

  • loading.tsx wraps the segment's page.tsx in <Suspense> automatically.
  • Wrap slow, independent sections in their own <Suspense> so the fast parts show first.
  • Streaming means the browser receives the shell immediately, then the HTML for each suspended part as its data resolves.

What / Why / When

What

Fallback UI plus progressive HTML.

Why

The user sees the page immediately instead of a blank screen while the slowest query finishes.

When

Any route with slow data, or pages with one slow section.

Use case

A post page whose comments load slowly.

Code compare

Next.js
// app/posts/loading.tsxexport default function Loading() {  return <p>Loading posts…</p>;}
Next.js
// app/posts/[slug]/page.tsximport { Suspense } from "react";import { Comments } from "./comments"; // async Server Component​export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {  const { slug } = await params;  const post = await getPost(slug);​  return (    <article>      <h1>{post.title}</h1>      <div>{post.body}</div>      <Suspense fallback={<p>Loading comments…</p>}>        <Comments slug={slug} />      </Suspense>    </article>  );}

@defer loads a component's code lazily and shows @loading / @placeholder blocks meanwhile. Server-side, deferred blocks render their placeholder unless you use incremental hydration:

Angular
<article>  <h1>{{ post().title }}</h1>  <div>{{ post().body }}</div>​  @defer (on viewport) {  <app-comments [slug]="slug()" />  } @placeholder {  <p>Comments</p>  } @loading (minimum 300ms) {  <p>Loading comments…</p>  }</article>

For loading state of the data itself, use the resource's status: @if (post.isLoading()) { … }.

Differences and gotchas

  • Next.js streams data; @defer defers code (and the work inside it). Angular SSR waits for pending HTTP requests before sending HTML; it doesn't stream sections.
  • loading.tsx is per route segment; @defer is per template block.

Quick reference

Next.jsAngular
loading.tsx@if (resource.isLoading()) / route-level spinner
<Suspense fallback>@defer { } @loading { } @placeholder { }
HTML streamingNot supported; SSR waits for app to be stable