← Back to the Build Your Homepage series
βš›οΈ
EPISODE 07
file-based routing Β· layouts Β· loading Β· error Β· dynamic routes

Next.js App Router

Master Next.js App Router: folders become routes, layouts wrap pages, dynamic segments handle slugs, and special files give you loading and error UI for free.

Next.jsApp Routerlayoutsloadingerror
Duration
⏱ About 2.5 hours
Level
πŸ“Š Intermediate+
Prerequisite
🎯 Lesson 5 + React 1–6
OUTCOME
A multi-page Next.js app with nested layouts, dynamic routes, and loading/error UI

What you'll learn

  • 1Map folders to routes (page.tsx, layout.tsx)
  • 2Nest layouts to share UI
  • 3Use dynamic segments ([slug]) and generateStaticParams
  • 4Add loading.tsx and error.tsx for built-in states

1. Folder Routing

text
app/
β”œβ”€β”€ layout.tsx       # wraps everything
β”œβ”€β”€ page.tsx         # /
β”œβ”€β”€ about/
β”‚   └── page.tsx     # /about
β”œβ”€β”€ blog/
β”‚   β”œβ”€β”€ page.tsx     # /blog
β”‚   β”œβ”€β”€ layout.tsx   # wraps /blog and nested
β”‚   └── [slug]/
β”‚       └── page.tsx # /blog/:slug

2. Layouts

tsx
// app/layout.tsx (root)
import "./globals.css";
import Link from "next/link";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <nav><Link href="/">Home</Link> Β· <Link href="/about">About</Link></nav>
        <main>{children}</main>
      </body>
    </html>
  );
}

Nested layouts inherit from the parent β€” you can have section-specific headers, sidebars, etc.

3. Dynamic Routes & Static Params

tsx
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await getAllPosts();
  return posts.map(p => ({ slug: p.slug }));
}

export default async function Post({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params;
  const post = await getPost(slug);
  if (!post) notFound();
  return <article><h1>{post.title}</h1>{post.body}</article>;
}

4. Special Files: loading & error

tsx
// app/blog/loading.tsx
export default function Loading() { return <p>Loading posts...</p>; }

// app/blog/error.tsx (must be a client component)
"use client";
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
  return (
    <div>
      <p>Something went wrong: {error.message}</p>
      <button onClick={reset}>Try again</button>
    </div>
  );
}
Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub β†—