Menu

5. Next.js Fundamentals

Next.js Master Roadmap - IT Technology

This chapter introduces Next.js fundamentals, covering what Next.js is, its advantages over plain React, project setup with create-next-app, file-based routing patterns, and navigation techniques using Link and useRouter.

No MCQ questions available for this chapter.

5. Next.js Fundamentals

5.1 Introduction

5.1.1 What is Next.js?

Next.js is a powerful React framework that extends the capabilities of React by providing built-in solutions for server-side rendering (SSR), static site generation (SSG), incremental static regeneration (ISR), and hybrid rendering approaches. It abstracts away complex configuration while preserving the flexibility of React, enabling developers to focus on building features rather than wiring up tooling.

Key features include file‑based routing, API routes, automatic image and font optimization, middleware support, and first‑class TypeScript integration. The framework leverages Turbopack for fast builds and includes a rich plugin ecosystem for extending functionality.

Example: To scaffold a new project with all recommended defaults, run:

npx create-next-app@latest my-app --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --use-npm

This command creates a project named my-app configured with TypeScript, Tailwind CSS, ESLint, the App Router, a src/ directory, and an import alias @/*.

5.1.2 Why Next.js?

Next.js offers several compelling advantages that make it a preferred choice for modern web applications:

  • SEO‑friendly: Pages can be pre‑rendered via SSR or SSG, ensuring search engines receive fully rendered HTML.
  • Performance: Automatic code splitting, image optimization (next/image), and font optimization reduce payload size and improve Core Web Vitals.
  • Developer Experience: Fast Refresh provides instant feedback during development, while file‑based routing eliminates the need for manual route configuration.
  • Full‑Stack Capabilities: API routes and Server Actions let you write backend logic alongside UI code, enabling a monorepo‑style approach.
  • Edge Deployment Ready: Built‑in support for Vercel Edge Functions and other edge runtimes allows low‑latency global delivery.

Example (Static Generation for SEO): An e‑commerce product page can be generated at build time using generateStaticParams:

export async function generateStaticParams() {
  const products = await fetchProducts(); // hypothetical data fetch
  return products.map(p => ({ slug: p.slug }));
}

This creates a static route for each product, improving SEO and reducing server load.

5.1.3 React vs Next.js

While React provides the UI library, Next.js adds the infrastructure needed for production‑grade applications:

Aspect React (Vanilla) Next.js
Rendering Client‑side only (CSR) SSR, SSG, ISR, hybrid
Routing Manual (e.g., react-router-dom) File‑based, automatic
Data Fetching Various libraries (SWR, React Query) Built‑in getStaticProps, getServerSideProps, use hook, Server Actions
Optimizations Manual setup (code splitting, lazy loading) Automatic code splitting, image/font optimization
Tooling Require webpack/Babel config Zero‑config, extensible via next.config.js

Example (Routing Setup): In a plain React app you need:

npm i react-router-dom

and wrap your app:

<BrowserRouter>
  <Routes>
    <Route path="/" element=<HomePage /> />
    <Route path="/about" element=<AboutPage /> />
  </Routes>
</BrowserRouter>

In Next.js, simply create app/page.tsx for / and app/about/page.tsx for /about—no extra imports or configuration.

5.2 Project Setup

5.2.1 create-next-app

The official CLI tool create-next-app streamlines project initialization. It offers an interactive wizard and a non‑interactive mode for CI/CD pipelines.

Common Options:

  • --typescript – Adds TypeScript configuration.
  • --tailwind – Installs and configures Tailwind CSS.
  • --eslint – Sets up ESLint with Next.js‑specific rules.
  • --app – Enables the App Router (recommended).
  • --src-dir – Places source files inside a src/ folder.
  • --import-alias "@/*" – Configures a path alias for cleaner imports.
  • --use-npm, --use-yarn, --use-pnpm – Chooses the package manager.

Interactive Example:

npx create-next-app@latest

The CLI will prompt you for each option. For automation, you can pass all flags directly as shown in the non‑interactive example above.

5.2.2 Project Structure

When using the src/ directory and the App Router, the default layout is:

src/
├── app/                    # App Router pages
│   ├── layout.tsx          # Root layout (shared UI)
│   ├── page.tsx            # Home page (/)
│   ├── globals.css         # Global stylesheet
│   └── products/
│       └── [id]/
│           └── page.tsx    # Dynamic route /products/:id
├── components/             # Reusable UI components
├── lib/                    # Utility functions, configs
├── hooks/                  # Custom React hooks
└── types/                  # TypeScript type definitions

Each folder under app/ corresponds to a URL segment. A page.tsx file makes that segment publicly accessible. Layout files (layout.tsx) nest UI around child pages, enabling shared headers, footers, or sidebars.

Example (Nested Dashboard Layout):

// src/app/dashboard/layout.tsx
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
  return (
    <section>
      <nav>/* Sidebar navigation */</nav>
      <main>{children}</main>
    </section>
  );
}

This layout wraps src/app/dashboard/page.tsx and any nested routes such as src/app/dashboard/settings/page.tsx, providing a consistent sidebar across the dashboard.

5.2.3 Configuration Files

Next.js projects are customized via several configuration files:

next.config.js
Controls images, rewrites, headers, experimental features, and more.
tsconfig.json
TypeScript compiler options, path aliases, strict mode.
tailwind.config.ts
Tailwind content paths, theme extensions, plugins.
.eslintrc.json
ESLint rules, including Next.js core‑web‑vitals plugin.

Image Remote Patterns Example: To allow images from an external CDN:

// next.config.js
module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'cdn.example.com',
        pathname: '/**',
      },
    ],
  },
};

Path Alias in tsconfig.json:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

With this setup, you can import components as import Button from '@/components/Button' without relative paths.

5.3 Routing

5.3.1 File-Based Routing

Next.js adopts a convention‑over‑configuration approach: each folder under app/ represents a URL segment, and a page.tsx file inside that folder makes the segment accessible.

  • app/about/page.tsx/about
  • app/blog/[slug]/page.tsx/blog/:slug
  • app/settings/profile/page.tsx/settings/profile

No additional routing configuration is required. The framework automatically creates a router that matches the file system hierarchy.

5.3.2 Nested Routes

Nesting folders creates nested URLs. Layout files allow you to wrap UI around groups of pages.

Example:

app/
└── shop/
    ├── layout.tsx          // Shared sidebar
    ├── page.tsx            // /shop (product list)
    └── cart/
        └── page.tsx        // /shop/cart

The shop/layout.tsx might contain:

export default function ShopLayout({ children }) {
  return (
    <>
      <aside>/* Sidebar with categories */</aside>
      <section>{children}</section>
    </>
  );
}

Both page.tsx and cart/page.tsx receive the same sidebar layout, ensuring consistent navigation.

5.3.3 Dynamic Routes

Dynamic segments are denoted by brackets: [param]. The router supplies the parameter via the params prop.

Basic Usage:

// app/blog/[slug]/page.tsx
export default function BlogPost({ params }: { params: { slug: string } }) {
  return <h1>Blog post: {params.slug}</h1>;
}

When visiting /blog/my-first-post, params.slug equals "my-first-post".

TypeScript (Next.js 15+ async params):

interface BlogPostProps {
  params: Promise<{ slug: string }>;
}
export default async function BlogPost({ params }: BlogPostProps) {
  const { slug } = await params;
  return <article>/* fetch data using slug */</article>;
}

The params object is now a Promise that resolves to the route parameters, enabling seamless integration with server‑side data fetching.

5.3.4 Catch‑All Routes

To match multiple segments, use the spread syntax [...slug]. This captures any number of path parts after the matched prefix.

Example: app/docs/[...slug]/page.tsx matches:

  • /docsparams.slug = []
  • /docs/getting-startedparams.slug = ['getting-started']
  • /docs/api/referenceparams.slug = ['api', 'reference']

Optional catch‑all [[...slug]] also matches the root without requiring a trailing segment, so /docs is still valid.

5.3.5 Route Groups

Parentheses (group) let you organize folders without affecting the URL path. This is useful for applying different layouts to distinct sections of the site.

Example Structure:

app/
├── (marketing)/
│   ├── layout.tsx          // Landing‑page header
│   ├── about/page.tsx      // /about
│   └── contact/page.tsx    // /contact
├── (dashboard)/
│   ├── layout.tsx          // Sidebar navigation
│   ├── page.tsx            // /dashboard
│   └── settings/page.tsx   // /dashboard/settings
└── (auth)/
    ├── layout.tsx          // Minimal layout
    ├── login/page.tsx      // /login
    └── register/page.tsx   // /register

Even though the folders are nested under different groups, all routes resolve to the same top‑level paths (/about, /login, etc.) because the parentheses are ignored in the URL.

5.4 Navigation

5.4.1 Link Component

The next/link module provides the Link component for client‑side navigation with automatic prefetching.

Basic Usage:

import Link from 'next/link';
<Link href="/products">Products</Link>

By default, Next.js prefetches the linked page in the background when the link enters the viewport, improving perceived performance.

Disabling Prefetch:

<Link href={`/products/${product.id}`} prefetch={false}>
  <ProductCard {...product} />
</Link>

Useful for large lists where prefetching every item would waste bandwidth.

Replace History Entry:

<Link href="/login" replace>Log in</Link>

The replace prop prevents adding a new entry to the browser history, which is handy for redirects after authentication.

5.4.2 useRouter

For programmatic navigation, the useRouter hook from next/navigation gives you imperative control over routing.

Common Methods:

  • router.push(url) – Navigate to a new URL, pushing onto history.
  • router.replace(url) – Navigate without adding a history entry.
  • router.back() – Go back to the previous page.
  • router.forward() – Go forward (if available).
  • router.refresh() – Revalidate the current page’s data.

Example (Form Submission):

import { useRouter } from 'next/navigation';
import { useState } from 'react';

export default function LoginForm() {
  const router = useRouter();
  const [isPending, setIsPending] = useState(false);

  const handleSubmit = async (formData) => {
    setIsPending(true);
    try {
      await fetch('/api/login', {
        method: 'POST',
        body: JSON.stringify(formData),
      });
      router.push('/dashboard');
      router.refresh(); // Ensure server data is up‑to‑date
    } finally {
      setIsPending(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* form fields */}
      <button type="submit" disabled={isPending}>
        {isPending ? 'Logging in…' : 'Log in'}
      </button>
    </form>
  );
}

After a successful login, the user is redirected to the dashboard and the page data is refreshed to reflect the authenticated state.

5.4.3 Programmatic Navigation (Server‑Side)

In Server Components or Server Actions, you can use the redirect function from next/navigation to perform a server‑side redirect.

Example (Protected Route):

// app/dashboard/page.tsx
import { redirect } from 'next/navigation';
import { getSession } from '@/lib/auth';

export default async function DashboardPage() {
  const session = await getSession();
  if (!session) {
    redirect('/login');
  }
  return <h1>Welcome to your dashboard</h1>;
}

If no session is found, the server responds with a redirect to /login before any HTML is streamed to the client.

Example (Server Action):

// app/actions.ts
'use server';
import { redirect } from 'next/navigation';

export async function updateProfile(formData: FormData) {
  // …perform update logic…
  redirect('/profile'); // client navigates after action completes
}

Server Actions enable seamless mutation‑and‑navigation patterns without writing explicit API routes.

Summary

This chapter has explored the core pillars of Next.js:

  • Understanding what Next.js adds to React (SSR/SSG, file‑based routing, optimizations).
  • Setting up a project with create-next-app and examining the generated structure.
  • Configuring the framework via next.config.js, tsconfig.json, Tailwind, and ESLint.
  • Mastering routing fundamentals: static, nested, dynamic, catch‑all, and route groups.
  • Implementing navigation with the declarative Link component and the imperative useRouter hook, plus server‑side redirects.

With these foundations, you are ready to build scalable, performant, and SEO‑friendly applications using Next.js.