C.W.K.
Stream
Lesson 05 of 08 · published

Why App Router Wins

~22 min · RSC, streaming, Suspense, parallel routes

Level 0Curious
0 XP0/68 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

It's a different architecture, not a new folder

The App Router is built on React Server Components, Suspense, and a streaming HTTP response model. None of those existed in the Pages Router era. The five things you actually get:

  1. Server Components by default. Components render on the server and ship zero JavaScript for that node. Heavy libraries (markdown, syntax highlighting, date formatters) stay server-side.
  2. Nested layouts that persist. The dashboard shell doesn't re-render when you click between dashboard tabs. State and DOM stay alive across navigations.
  3. Streaming & Suspense. The page sends HTML in pieces — the cheap parts arrive instantly, slow data streams in. No full-page spinner.
  4. Parallel routes. Multiple route segments render at once into the same layout, each with its own loading/error boundary.
  5. Server Actions. Mutations live on the server, called directly from forms. No /api/… route table for CRUD.

What this changes for the team

You stop writing API routes for every server-side need. You stop wrapping pages in client-side data libraries that refetch on every navigation. You start composing UI from server-default leaves with islands of 'use client' where interactivity actually lives.

Code

A nested layout that persists across child navigations·tsx
// app/dashboard/layout.tsx
export default function DashboardLayout({
  children,
  analytics,
}: {
  children: React.ReactNode;
  analytics: React.ReactNode; // parallel slot @analytics
}) {
  return (
    <div className="grid grid-cols-[240px_1fr_320px]">
      <Sidebar />
      <main>{children}</main>
      <aside>{analytics}</aside>
    </div>
  );
}

// app/dashboard/loading.tsx
export default function Loading() {
  return <DashboardSkeleton />;
}
Streaming with explicit Suspense islands·tsx
import { Suspense } from 'react';

export default function Page() {
  return (
    <>
      <Header /> {/* fast, server-rendered */}
      <Suspense fallback={<StatsSkeleton />}>
        <Stats />        {/* slow query, streams in */}
      </Suspense>
      <Suspense fallback={<ChartSkeleton />}>
        <RevenueChart /> {/* independent stream */}
      </Suspense>
    </>
  );
}

External links

Exercise

Pick a route in your app (or the starter) that has a slow data fetch. Add a loading.tsx and a Suspense boundary around the slow leaf. Capture before/after screenshots of the perceived load.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.