Learning Next.js - Routing & Navigation
Episode 4 of 24

Learning Next.js - Routing & Navigation

This episode digs into routing in the App Router: page and layout files, dynamic routes, catch-all routes, optional catch-all, the Link component and useRouter for navigation, and the metadata API for SEO and page descriptions.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Now that your project is up from episode 3, it's time to give the application more than one page. Routing determines how URLs map to code, and navigation determines how users move between pages.

Episode 4 digs into the App Router routing system: the segment concept, page.tsx and layout.tsx files, dynamic routes with parameters, catch-all routes that capture many segments, the Link component for client-side navigation, and the metadata API for SEO.

Routing Basics in the App Router

Segment, page, and layout

Every folder inside app is a segment that corresponds to a part of the URL. The folder app/about/page.tsx automatically becomes the /about page, with no manual router registration needed. This is the file-system routing pattern that makes the application structure visible directly from the folders.

A layout is a shared frame. The layout in the app/blog folder wraps all the pages beneath it:

Blog segment layout
export default function BlogLayout({ children }) {
  return (
    <section>
      <nav>Navigasi Blog</nav>
      {children}
    </section>
  )
}

The file above is app/blog/layout.tsx. The children content is filled with the active page — so the blog navbar appears on every blog page without being written again.

Dynamic Routes, Catch-All, and Optional Catch-All

Dynamic Routes with Parameters

For pages that depend on a dynamic value like an article slug, create a folder with square brackets. The file app/blog/[slug]/page.tsx receives params containing the slug:

Dynamic route with params
export default async function BlogPost({ params }) {
  const { slug } = await params
  return (
    <article>
      <h1>Artikel dengan slug: {slug}</h1>
    </article>
  )
}

The await params above is required in Next.js 15 because params is a Promise. When you open /blog/pengenalan-react, the slug value is pengenalan-react.

Catch-All and Optional Catch-All

If a single page must handle any number of segments, use a catch-all with three dots. The folder app/docs/[...slug]/page.tsx captures URLs like /docs/getting-started/intro. To also capture /docs with no segment, use the optional catch-all: the folder app/docs/[[...slug]]/page.tsx. The only difference is the extra set of square brackets around the parameter.

generateStaticParams

For dynamic pages that use SSG, register all the parameter values at build time:

generateStaticParams for SSG
export async function generateStaticParams() {
  return [
    { slug: "pengenalan-react" },
    { slug: "panduan-nextjs" },
  ]
}

The function generateStaticParams returns the list of parameters that will be prerendered during npm run build. We'll cover the full details in episode 6.

To move between pages without reloading the whole document, use the Link component. Import it from next/link and use the href prop:

Navigation with Link
import Link from "next/link"
 
export default function Navbar() {
  return (
    <nav>
      <Link href="/">Beranda</Link>
      <Link href="/blog">Blog</Link>
      <Link href="/blog/pengenalan-react">Artikel</Link>
    </nav>
  )
}

Link triggers prefetching: Next.js loads the destination page ahead of time when the link becomes visible in the viewport, making navigation feel instant. When the user clicks, the transition happens without a full reload.

useRouter for Programmatic Navigation

For navigation triggered from code — for example after a form is submitted — use the useRouter hook:

useRouter for redirect
"use client"
 
import { useRouter } from "next/navigation"
 
export default function LoginButton() {
  const router = useRouter()
 
  return (
    <button onClick={() => router.push("/dashboard")}>
      Masuk Dashboard
    </button>
  )
}

router.push("/dashboard") redirects the user programmatically. Note the "use client" directive on the first line — hooks like useRouter only run in client components, a concept we'll break down in episode 7.

Metadata and SEO

The Metadata API

The App Router provides a metadata API for controlling the title, description, and meta tags of every page. Export a metadata object from layout.tsx or page.tsx:

Metadata with the Metadata API
import type { Metadata } from "next"
 
export const metadata: Metadata = {
  title: "Beranda | Aplikasi Saya",
  description: "Halaman beranda aplikasi Next.js",
  openGraph: {
    title: "Aplikasi Saya",
    description: "Halaman beranda aplikasi Next.js",
  },
}

The metadata object above produces the title and description tags for SEO as well as Open Graph tags for social media. For dynamic pages, the generateMetadata function can generate metadata based on params — an advanced topic in episode 17.

Closing

Here's what to take away:

  • The App Router maps folders to URLs automatically.
  • Dynamic routes use square brackets; catch-alls use three dots.
  • generateStaticParams sets the list of parameters for SSG.
  • The Link component provides client-side navigation with prefetching.
  • useRouter handles programmatic navigation from code.
  • The metadata API controls per-page SEO and descriptions.

In the next episode, episode 5, we'll discuss components and layouts — reusable components, nested layouts and templates, shared layouts for dynamic segments, loading UI with loading files, and styling with CSS modules, Tailwind CSS, and styled-components. Your application's UI structure will start to mature.

Learning Next.js - Routing & Navigation | Learn Next.js