Topic 02 / 13

Routing — Pages, Layouts & Navigation

~10 min read  //  Next.js Series  //  Coding India

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 styling

Organisational extras: route groups (marketing)/about/page.js group folders without affecting URLs; private folders _components/ are never routable.