beginner Step 2 of 15

Pages and Routing

Next.js Development

Pages and Routing

Next.js uses a file-system-based routing mechanism where the file and folder structure inside the app directory automatically maps to URL paths. Each folder represents a route segment, and a page.tsx file inside a folder makes that route publicly accessible. This eliminates the need for a separate routing configuration file and makes the relationship between URLs and code immediately visible in the project structure. Next.js routing supports dynamic segments, catch-all routes, route groups, and parallel routes for complex application architectures.

Basic File-Based Routing

Every page.tsx file in the app directory creates a route. The file path determines the URL path.

// File structure → URL mapping
// app/page.tsx              → /
// app/about/page.tsx        → /about
// app/contact/page.tsx      → /contact
// app/blog/page.tsx         → /blog
// app/dashboard/settings/page.tsx → /dashboard/settings

// app/about/page.tsx
export default function AboutPage() {
  return (
    <div>
      <h1>About Us</h1>
      <p>This is the about page of our Next.js application.</p>
    </div>
  );
}

Dynamic Routes

Square brackets in folder names create dynamic route segments that capture URL parameters. These parameters are passed to the page component as props.

// app/blog/[slug]/page.tsx → /blog/my-first-post
// app/users/[id]/page.tsx  → /users/42

// app/blog/[slug]/page.tsx
interface BlogPostProps {
  params: Promise<{ slug: string }>;
}

export default async function BlogPost({ params }: BlogPostProps) {
  const { slug } = await params;

  // Fetch post data based on the slug
  const post = await fetch(`https://api.example.com/posts/${slug}`).then(
    (r) => r.json()
  );

  return (
    <article>
      <h1>{post.title}</h1>
      <p>Slug: {slug}</p>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
    </article>
  );
}

// app/users/[id]/page.tsx
interface UserPageProps {
  params: Promise<{ id: string }>;
}

export default async function UserPage({ params }: UserPageProps) {
  const { id } = await params;
  return <h1>User Profile: {id}</h1>;
}

Catch-All and Optional Catch-All Routes

// Catch-all: app/docs/[...slug]/page.tsx
// Matches: /docs/a, /docs/a/b, /docs/a/b/c
// params.slug = ["a", "b", "c"]

// Optional catch-all: app/docs/[[...slug]]/page.tsx
// Also matches: /docs (slug is undefined)

interface DocsProps {
  params: Promise<{ slug?: string[] }>;
}

export default async function DocsPage({ params }: DocsProps) {
  const { slug } = await params;
  const path = slug ? slug.join("/") : "index";
  return <h1>Documentation: {path}</h1>;
}

Navigation

Next.js provides the Link component for client-side navigation and the useRouter hook for programmatic navigation. The Link component prefetches linked pages for instant navigation.

"use client";
import Link from "next/link";
import { useRouter, usePathname } from "next/navigation";

export default function Navigation() {
  const router = useRouter();
  const pathname = usePathname();

  function handleLogout() {
    // Clear session and redirect
    router.push("/login");
  }

  return (
    <nav>
      {/* Declarative navigation */}
      <Link href="/" className={pathname === "/" ? "active" : ""}>
        Home
      </Link>
      <Link href="/about">About</Link>
      <Link href="/blog">Blog</Link>

      {/* Dynamic route link */}
      <Link href={`/users/${42}`}>My Profile</Link>

      {/* Programmatic navigation */}
      <button onClick={() => router.push("/dashboard")}>
        Dashboard
      </button>
      <button onClick={() => router.back()}>Go Back</button>
      <button onClick={handleLogout}>Logout</button>
    </nav>
  );
}

Route Groups

Folders wrapped in parentheses create route groups that organize routes without affecting the URL path. This is useful for applying different layouts to different sections of your app.

// Route groups — parentheses in folder names
// app/(marketing)/about/page.tsx    → /about
// app/(marketing)/pricing/page.tsx  → /pricing
// app/(app)/dashboard/page.tsx      → /dashboard
// app/(app)/settings/page.tsx       → /settings

// Each group can have its own layout:
// app/(marketing)/layout.tsx — marketing layout with hero header
// app/(app)/layout.tsx       — app layout with sidebar
Tip: Use the Link component instead of anchor tags for internal navigation. Link automatically prefetches the linked page in production, making navigation feel instant. For external links, use regular <a> tags.

Key Takeaways

  • File-based routing maps folder/file structure to URL paths automatically.
  • Dynamic routes use [param] folders to capture URL segments as parameters.
  • Catch-all routes ([...slug]) match multiple path segments.
  • Use Link for declarative navigation and useRouter for programmatic navigation.
  • Route groups (groupName) organize routes and layouts without affecting URLs.