Next.jsReactArchitecture

Practical Patterns for the Next.js App Router

Alejandro Gómez8 min read

A deep-dive into Server Components, nested layouts, and parallel routes — with real-world examples from production apps.

Why the App Router changes everything

With the Pages Router, every page was a full client-side React tree. The App Router flips this: everything is a Server Component by default, and you opt-in to the client only where you need interactivity.

This matters for three concrete reasons: smaller JS bundles (server code never ships), instant access to async data without useEffect waterfalls, and co-location of layout logic with the components that need it.

Server Components in practice

The key mental model: a Server Component is just an async function. You can await a database query or a CMS fetch directly inside the component — no getServerSideProps, no API route wrapper.

// app/dashboard/page.tsx
export default async function DashboardPage() {
  const metrics = await db.query('SELECT * FROM metrics');
  return <MetricsGrid data={metrics} />;
}

The data fetching is co-located, the component tree is leaner, and the client bundle stays small because MetricsGrid and its dependencies never ship to the browser.

Nested layouts and the loading hierarchy

Nested layouts are one of the App Router's killer features. Each segment of the URL can have its own layout.tsx, and React renders them as a nested tree — so navigating between sibling pages doesn't re-mount shared layout UI.

  • Use layout.tsx for persistent shell UI (sidebars, tabs)
  • Use loading.tsx for per-segment suspense skeletons
  • Use error.tsx for per-segment error boundaries
  • Use template.tsx when you need a fresh mount on every navigation

Parallel routes for complex UIs

Parallel routes let you render multiple independent page slots simultaneously. They're perfect for dashboard layouts where a sidebar, main panel, and notification feed each have their own loading and error states.

app/dashboard/
  layout.tsx          ← wraps both slots
  @feed/page.tsx      ← parallel slot
  @main/page.tsx      ← parallel slot
Treat parallel routes as independent async trees. Each can load, error, and suspend without affecting the other — which is exactly what a dashboard needs.