Skip to main content

Learning Objectives

By the end of this section, you will be able to:
  • Explain the key differences between Next.js App Router and traditional React applications
  • Understand the concept of server components and client components
  • Describe the file-based routing system and its benefits
  • Identify when to use server vs client components
  • Understand the new data fetching patterns in Next.js
Duration: 4-5 hours

Part 1: Strategic Analysis & Understanding

Understanding Next.js App Router

Next.js App Router represents a fundamental shift in how we build React applications. Unlike traditional React apps that run entirely in the browser, Next.js App Router introduces a hybrid approach that combines server-side and client-side rendering.

Traditional React vs Next.js App Router

Traditional React (Create React App)

  • Entire app runs in the browser - All components are client components - JavaScript bundle sent to browser - SEO and performance challenges - Manual routing configuration

Next.js App Router

  • Hybrid server/client architecture - Server components by default - Optimized bundle splitting - Built-in SEO and performance - File-based routing system

Key Benefits of App Router

1

Better Performance

Server components reduce JavaScript bundle size and improve initial page load times
2

Enhanced SEO

Server-side rendering provides better search engine optimization
3

Simplified Data Fetching

Direct database access from server components eliminates API calls
4

Improved Developer Experience

File-based routing and built-in optimizations streamline development

Server Components vs Client Components

Understanding the distinction between server and client components is crucial for effective Next.js development.

Server Components

Server components run on the server and are rendered before being sent to the browser:

When to Use Server Components

  • Data fetching from databases - Accessing backend resources - Large dependencies - Sensitive information - Static content

Server Component Benefits

  • Reduced bundle size - Better performance - Direct database access - Enhanced security - SEO optimization

Client Components

Client components run in the browser and provide interactivity:

When to Use Client Components

  • Event handlers (onClick, onChange) - Browser APIs (localStorage, geolocation) - State management (useState, useEffect) - Interactive features
  • Third-party libraries

Client Component Features

  • Event handling - State management - Browser API access - Real-time updates
  • User interactions

File-Based Routing System

Next.js App Router uses a file-based routing system that automatically creates routes based on your file structure.

Overview

Next.js App Router uses a file-based routing system where the file structure in the app directory directly maps to URL routes. This approach simplifies routing configuration and provides better performance through automatic code splitting.

Route Segments

Each folder in the app directory represents a route segment:
app/
├── page.tsx          # / (root route)
├── about/
│   └── page.tsx      # /about
├── blog/
│   ├── page.tsx      # /blog
│   └── [slug]/
│       └── page.tsx  # /blog/[slug] (dynamic route)
└── dashboard/
    ├── page.tsx      # /dashboard
    └── settings/
        └── page.tsx  # /dashboard/settings

Essential Special Files

page.tsx

  • Makes a route publicly accessible
  • Required for a route to be accessible
  • Defines the UI for that route

layout.tsx

  • Shared UI for multiple pages
  • Wraps child layouts and pages
  • Maintains state during navigation

loading.tsx

  • Loading UI for a route segment
  • Shows while page is loading
  • Automatically wraps page and children

not-found.tsx

  • 404 page for a route segment
  • Shows when route is not found
  • Can be nested at different levels

Basic Navigation

import Link from "next/link";

export default function Navigation() {
  return (
    <nav>
      <Link href="/">Home</Link>
      <Link href="/about">About</Link>
      <Link href="/blog/hello-world">Blog Post</Link>
    </nav>
  );
}

Programmatic Navigation

"use client";

import { useRouter } from "next/navigation";

export default function MyComponent() {
  const router = useRouter();

  const handleClick = () => {
    router.push("/dashboard");
    // or
    router.replace("/login");
    // or
    router.back();
  };

  return <button onClick={handleClick}>Navigate</button>;
}

Basic Route Organization

Simple Structure

app/
├── layout.tsx            # Root layout
├── page.tsx              # Home page
├── about/
│   └── page.tsx          # About page
└── contact/
    └── page.tsx          # Contact page

Nested Routes

app/
├── layout.tsx            # Root layout
├── page.tsx              # Home page
└── dashboard/
    ├── layout.tsx        # Dashboard layout
    ├── page.tsx          # /dashboard
    └── settings/
        └── page.tsx      # /dashboard/settings
Key Takeaway: Next.js file-based routing simplifies route management by using the file system structure to define routes. Start with basic page.tsx and layout.tsx files, then add loading.tsx and not-found.tsx as needed.

Data Fetching Patterns

Next.js App Router introduces new patterns for data fetching that are more efficient than traditional approaches.

Server Component Data Fetching

// app/products/page.js
async function ProductsPage() {
  // Direct database access in server component
  const products = await db.products.findMany();

  return (
    <div>
      <h1>Products</h1>
      {products.map((product) => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}

Client Component Data Fetching

// app/components/ProductList.js
"use client";

import { useState, useEffect } from "react";

export default function ProductList() {
  const [products, setProducts] = useState([]);

  useEffect(() => {
    fetch("/api/products")
      .then((res) => res.json())
      .then((data) => setProducts(data));
  }, []);

  return (
    <div>
      {products.map((product) => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}

Overview

Next.js App Router introduces a new component model that distinguishes between server and client components. This distinction is crucial for optimizing performance, reducing bundle size, and improving user experience.

Server Components (Default)

Server components run on the server and are rendered before being sent to the browser. They have access to server-side resources and don’t include JavaScript in the client bundle.

Key Characteristics

Server-Side Rendering

  • Rendered on the server
  • HTML sent to browser
  • No JavaScript in client bundle
  • Direct database access

Performance Benefits

  • Reduced bundle size
  • Faster initial page load
  • Better SEO
  • Enhanced security

When to Use Server Components

  • Data Fetching: Fetch data from databases, APIs, or file systems
  • Static Content: Display content that doesn’t change frequently
  • Large Dependencies: Use libraries that are large or server-only
  • Sensitive Operations: Handle operations that shouldn’t be exposed to the client

Example: Server Component

// app/dashboard/page.tsx (Server Component)
import { db } from "@/lib/database";

export default async function Dashboard() {
  // Direct database access on the server
  const stats = await db.getDashboardStats();

  return (
    <div>
      <h1>Dashboard</h1>
      <div className="stats-grid">
        {stats.map((stat) => (
          <div key={stat.id} className="stat-card">
            <h3>{stat.title}</h3>
            <p>{stat.value}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

Client Components

Client components run in the browser and provide interactivity. They include JavaScript in the client bundle and can use browser APIs and React hooks.

Key Characteristics

Client-Side Rendering

  • Rendered in the browser
  • Includes JavaScript bundle
  • Hydration required
  • Interactive features

Use Cases

  • Event handlers
  • State management
  • Browser APIs
  • Real-time updates

When to Use Client Components

  • Interactivity: Components that need event handlers (onClick, onChange)
  • Browser APIs: Access to localStorage, geolocation, camera, etc.
  • State Management: Components using useState, useEffect, or other hooks
  • Third-Party Libraries: Libraries that require browser APIs

Example: Client Component

"use client";

import { useState, useEffect } from "react";

export default function InteractiveCounter() {
  const [count, setCount] = useState(0);
  const [user, setUser] = useState(null);

  useEffect(() => {
    // Access browser APIs
    const savedUser = localStorage.getItem("user");
    if (savedUser) {
      setUser(JSON.parse(savedUser));
    }
  }, []);

  const handleIncrement = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <h2>Interactive Counter</h2>
      <p>Count: {count}</p>
      <button onClick={handleIncrement}>Increment</button>
      {user && <p>Welcome, {user.name}!</p>}
    </div>
  );
}

The ‘use client’ Directive

The 'use client' directive tells Next.js that a component should run on the client side.

Important Rules

  • Must be at the top: The directive must be the very first line in the file
  • No code before it: Nothing can come before the directive
  • File-level scope: Applies to the entire file and all its exports
"use client"; // Must be first line

import { useState } from "react";

export default function ClientComponent() {
  const [state, setState] = useState("");
  return <div>Interactive content</div>;
}

Basic Component Composition

Server Component with Client Children

// Server Component (app/dashboard/page.tsx)
import { db } from "@/lib/database";
import InteractiveChart from "@/components/InteractiveChart"; // Client component

export default async function Dashboard() {
  const data = await db.getChartData();

  return (
    <div>
      <h1>Dashboard</h1>
      {/* Server-rendered content */}
      <div className="stats">
        <p>Total Users: {data.totalUsers}</p>
        <p>Active Sessions: {data.activeSessions}</p>
      </div>

      {/* Client component for interactivity */}
      <InteractiveChart data={data} />
    </div>
  );
}

Best Practices for Module 1

Component Design

  1. Start with Server Components: Default to server components unless client features are needed
  2. Minimize Client Boundaries: Keep client components small and focused
  3. Use Composition: Compose server and client components effectively
  4. Add ‘use client’ Only When Necessary: Only use the directive when you need interactivity
Key Takeaway: The key to optimal Next.js performance is using server components by default and only adding client components where interactivity is required. This approach reduces bundle size and improves user experience.

Real-World Example: VSL Service Center

Let’s examine how the VSL Service Center application would benefit from Next.js App Router:

Current React Structure (Create React App)

src/
├── components/
│   ├── Login/
│   ├── Dashboard/
│   ├── Reports/
│   └── Transactions/
├── services/
│   └── api.js
└── App.js

Next.js App Router Structure

app/
├── (auth)/
│   ├── login/
│   │   └── page.js
│   └── layout.js
├── dashboard/
│   ├── page.js
│   └── layout.js
├── reports/
│   ├── page.js
│   ├── [reportId]/
│   │   └── page.js
│   └── layout.js
├── transactions/
│   ├── page.js
│   ├── inward/
│   │   └── page.js
│   └── outward/
│       └── page.js
└── layout.js

Migration Benefits for VSL Service Center

Performance Improvements

  • Faster initial page loads - Reduced JavaScript bundle size - Better caching strategies - Optimized data fetching

SEO & Accessibility

  • Better search engine visibility - Improved accessibility - Social media sharing - Progressive enhancement

Developer Experience

  • Simplified routing - Built-in optimizations - Better error handling - Enhanced debugging

Scalability

  • Server-side rendering - Edge computing support - Automatic code splitting
  • Performance monitoring

Part 2: Hands-On Implementation

Exercise 1: Create Your First Next.js App

  1. Create a new Next.js project:
    terminal
    npx create-next-app@latest vsl-migration --typescript --tailwind --eslint --app
    cd vsl-migration
    
  2. Explore the project structure:
    • Examine the app/ directory
    • Look at the page.js and layout.js files
    • Understand the file-based routing
  3. Create a simple server component:
    app/dashboard/page.js
    export default function Dashboard() {
      return (
        <div>
          <h1>VSL Service Center Dashboard</h1>
          <p>This is a server component</p>
        </div>
      );
    }
    
  4. Create a client component:
    app/components/InteractiveButton.js
    "use client";
    
    import { useState } from "react";
    
    export default function InteractiveButton() {
      const [count, setCount] = useState(0);
    
      return (
        <button onClick={() => setCount(count + 1)}>
          Clicked {count} times
        </button>
      );
    }
    

Exercise 2: Analyze VSL Service Center Structure

  1. Examine the current VSL Service Center structure:
    • Review the component hierarchy
    • Identify server vs client component candidates
    • Plan the new routing structure
  2. Create a migration plan:
    • List components that can be server components
    • Identify components that need to be client components
    • Plan the new file structure

Part 3: Reflection & Assessment

Self-Assessment Quiz

Test your understanding of Next.js App Router concepts:
  1. What is the main difference between server and client components?
    • Server components run on the server, client components run in the browser
    • Server components are faster, client components are slower
    • Server components use less memory, client components use more memory
    • There is no difference between them
  2. Which file creates a route in Next.js App Router?
    • route.js
    • page.js
    • component.js
    • index.js
  3. When should you use a client component?
    • For data fetching from databases
    • For static content
    • For event handlers and interactivity
    • For SEO optimization
  4. What is the benefit of server components?
    • They provide better interactivity
    • They reduce JavaScript bundle size
    • They offer better state management
    • They provide real-time updates
  5. How does file-based routing work in Next.js?
    • You manually configure routes in a config file
    • Routes are automatically created based on file structure
    • You use a routing library like React Router
    • Routes are defined in the component files

Reflection Questions

Take a moment to reflect on what you’ve learned:
  1. How would migrating to Next.js App Router benefit the VSL Service Center application?
    • Consider performance improvements
    • Think about SEO and accessibility benefits
    • Reflect on developer experience improvements
  2. Which components in the VSL Service Center would be good candidates for server components?
    • Think about components that fetch data
    • Consider static content components
    • Identify components that don’t need interactivity
  3. What challenges might you face when migrating from Create React App to Next.js?
    • Consider component compatibility
    • Think about routing changes
    • Reflect on data fetching patterns

Extension Activities

  1. Plan Your Migration Strategy:
    • Create a component inventory for VSL Service Center
    • Identify which components need to be client vs server
    • Plan the new file structure
  2. Explore Next.js Features:
    • Research new App Router capabilities
    • Study server actions and form handling
    • Learn about streaming and suspense patterns

Next Steps

You’ve now understood the foundational concepts of Next.js App Router! In the next section, you’ll learn about:
  • Project setup and configuration for enterprise applications
  • Migration strategy development for complex applications
  • Environment configuration and development tools
Key Takeaway: Next.js App Router provides a modern, performant approach to building React applications with server-side rendering, file-based routing, and optimized data fetching patterns.