Routing — Pages, Layouts & Navigation
The File Conventions
Inside any route folder, special filenames have fixed jobs:
app/
├── layout.js # shared shell — wraps this segment and below
├── page.js # the routable UI for this URL
├── loading.js # instant loading UI (automatic Suspense)
├── error.js # error boundary for this segment
└── not-found.js # rendered by notFound()The Root Layout
Every app has one — it owns <html> and <body>:
// app/layout.js
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<Navbar />
<main>{children}</main>
<Footer />
</body>
</html>
);
}Nested Layouts
Layouts stack. A dashboard section with its own sidebar:
app/
├── layout.js # navbar + footer (everything)
└── dashboard/
├── layout.js # + sidebar (dashboard pages only)
├── page.js # /dashboard
└── settings/
└── page.js # /dashboard/settings// app/dashboard/layout.js
export default function DashboardLayout({ children }) {
return (
<div className="dash">
<Sidebar />
<section>{children}</section>
</div>
);
}Key behaviour: when navigating between /dashboard and /dashboard/settings, the layouts don’t re-render or lose state — only the page swaps. This is what makes the App Router feel app-like.
loading.js — Free Streaming UX
// app/dashboard/loading.js
export default function Loading() {
return <Skeleton />; // shows instantly while page.js fetches data
}Next wraps the page in a Suspense boundary automatically — the shell renders immediately, the slow part streams in.
error.js — Scoped Error Boundaries
// app/dashboard/error.js
"use client"; // error components must be client components
export default function Error({ error, reset }) {
return (
<div>
<p>Something went wrong: {error.message}</p>
<button onClick={() => reset()}>Try again</button>
</div>
);
}A crash in the dashboard shows this UI inside the layout — the navbar keeps working.
Navigation
import Link from "next/link";
<Link href="/blog">Blog</Link>
<Link href={`/courses/${course.slug}`}>{course.title}</Link>Link prefetches routes in the viewport and navigates client-side — instant transitions. Programmatic navigation in client components:
"use client";
import { useRouter, usePathname } from "next/navigation";
const router = useRouter();
router.push("/dashboard");
router.refresh(); // re-fetch server data for the current route
const pathname = usePathname(); // for active nav stylingOrganisational extras: route groups (marketing)/about/page.js group folders without affecting URLs; private folders _components/ are never routable.