Next.js App Router — Complete Tutorial

Sanjeev SharmaSanjeev Sharma
4 min read

Advertisement

Next.js App Router — Complete Tutorial

The App Router represents a fundamental shift in how Next.js applications are structured. This tutorial covers everything from basic routing to advanced patterns.

Understanding File-Based Routing

Next.js uses the file system as your router. Files in the app directory automatically become routes.

app/
├── page.tsx/
├── about/
│   └── page.tsx/about
├── blog/
│   ├── page.tsx/blog
│   └── [slug]/
│       └── page.tsx/blog/[slug]
└── api/
    └── posts/
        └── route.tsPOST /api/posts

Basic Route Creation

Creating a route is as simple as adding a file:

// app/about/page.tsx
export default function AboutPage() {
  return (
    <div>
      <h1>About Us</h1>
      <p>Learn more about our company.</p>
    </div>
  )
}

Visit /about and your component renders instantly.

Dynamic Routes with Params

Dynamic segments capture variable parts of URLs:

// app/blog/[slug]/page.tsx
interface Params {
  slug: string
}

export default function BlogPost({ params }: { params: Params }) {
  return (
    <article>
      <h1>Blog Post: {params.slug}</h1>
      <p>Full post content loads here.</p>
    </article>
  )
}

A request to /blog/hello-world passes slug: "hello-world" to the component.

Nested Layouts

Layouts wrap multiple pages with shared UI:

// app/blog/layout.tsx
export default function BlogLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <div className="flex gap-8">
      <aside className="w-48">
        <nav>
          <a href="/blog">All Posts</a>
          <a href="/blog/tips">Tips</a>
          <a href="/blog/tutorials">Tutorials</a>
        </nav>
      </aside>
      <main className="flex-1">{children}</main>
    </div>
  )
}

This layout applies to /blog and all nested routes.

Route Groups for Organization

Parentheses organize routes without affecting URLs:

app/
├── (marketing)/
│   ├── page.tsx/
│   ├── about/
│   │   └── page.tsx/about
│   └── layout.tsx        → shared layout
├── (dashboard)/
│   ├── layout.tsx        → different layout
│   ├── page.tsx/
│   └── analytics/
│       └── page.tsx/analytics

Catch-All Routes

Triple dots capture all remaining segments:

// app/docs/[[...slug]]/page.tsx
interface Params {
  slug?: string[]
}

export default function DocsPage({ params }: { params: Params }) {
  const path = params.slug?.join('/') || 'home'
  return <div>Viewing docs: {path}</div>
}

Both /docs and /docs/guides/getting-started match this route.

Parallel Routes

Render multiple segments simultaneously:

// app/@modal/(..)post/[id]/page.tsx
// app/@sidebar/page.tsx
// app/layout.tsx

export default function Layout({
  children,
  modal,
  sidebar,
}: {
  children: React.ReactNode
  modal: React.ReactNode
  sidebar: React.ReactNode
}) {
  return (
    <div>
      <div className="flex">
        {sidebar}
        <main>{children}</main>
      </div>
      {modal}
    </div>
  )
}

Intercepting Routes

Intercept routes to show content in modals:

// app/(.)post/[id]/page.tsx
export default function PostModal({ params }: { params: { id: string } }) {
  return (
    <div className="modal">
      <h2>Post {params.id}</h2>
      <p>Rendered as a modal when navigated from certain pages.</p>
    </div>
  )
}

Linking Between Routes

Use the Link component for client-side navigation:

import Link from 'next/link'

export default function Navigation() {
  return (
    <nav>
      <Link href="/">Home</Link>
      <Link href="/about">About</Link>
      <Link href="/blog/hello-world">Blog Post</Link>
    </nav>
  )
}

Programmatic Navigation

Use the useRouter hook for dynamic navigation:

'use client'

import { useRouter } from 'next/navigation'

export default function LoginForm() {
  const router = useRouter()

  async function handleSubmit(e: FormData) {
    const user = await fetch('/api/login', { method: 'POST', body: e })
    if (user.ok) {
      router.push('/dashboard')
    }
  }

  return <form action={handleSubmit}>...</form>
}

Route Metadata

Define page metadata for SEO:

// app/blog/[slug]/page.tsx
export const metadata = {
  title: 'Blog Post',
  description: 'Read our latest blog post',
}

export default function Page() {
  return <article>...</article>
}

FAQ

Q: How do I handle 404 pages? A: Create a not-found.tsx file in any directory. Next.js shows this when a route doesn't match.

Q: Can I use regex in dynamic routes? A: No, but you can use catch-all routes [[...slug]] and validate segments in your component.

Q: What's the difference between /app/blog/[id] and /app/blog/(post)/[id]? A: The first creates a route /blog/[id]. The second with parentheses also creates /blog/[id] but groups files without affecting the URL structure. Use groups to organize related pages.


Master the App Router and you've mastered Next.js's core routing system.

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro