Next.js · Lesson 2 of 5
Routing, Layouts and Navigation
Dynamic routes, nested layouts, Link and loading states.
- Beginner
- 15 min read
- 3 objectives
Before this lessonLesson 1: Introduction and Project Setup
What you will learn
- Create dynamic routes
- Nest layouts
- Use loading and not-found files
The App Router gives you a small set of special file names. Learn them and you can build almost any navigation structure.
Dynamic routes
Wrap a folder name in square brackets to capture that part of the URL as a parameter.
// app/blog/[slug]/page.tsx -> /blog/hello-world
export default async function Post({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
return <h1>Post: {slug}</h1>;
}[...slug] catches any number of segments; [[...slug]] also matches the bare path. In recent Next.js versions params is a promise, so await it.
Nested layouts
A layout.tsx in any folder wraps all pages beneath it, and layouts nest. A dashboard can have its own sidebar without affecting the marketing pages.
// app/dashboard/layout.tsx
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<div style={{ display: "flex" }}>
<aside>Sidebar</aside>
<section>{children}</section>
</div>
);
}Route groups
Folders in parentheses organize code without adding to the URL: app/(marketing)/about/page.tsx is still /about. They also let different groups use different layouts.
Special files
loading.tsx: shown instantly while a page's data loads (uses React Suspense).error.tsx: shown when something inside throws. It must be a client component.not-found.tsx: shown for a missing page; callnotFound()to trigger it.route.ts: an API endpoint instead of a page.
// app/blog/[slug]/loading.tsx
export default function Loading() {
return <p>Loading post...</p>;
}
// app/blog/[slug]/page.tsx
import { notFound } from "next/navigation";
export default async function Post({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const post = await getPost(slug); // your data function
if (!post) notFound();
return <article><h1>{post.title}</h1></article>;
}Navigation
"use client";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
export function NavLink({ href, children }: { href: string; children: React.ReactNode }) {
const active = usePathname() === href;
return <Link href={href} className={active ? "active" : ""}>{children}</Link>;
}
export function BackButton() {
const router = useRouter();
return <button onClick={() => router.back()}>Back</button>;
}// Write your solution here
