Skip to main content

Overview

This page contains the supportive information and domain knowledge areas that are essential for Module 1: Next.js Foundation & Project Setup. These resources provide the foundational knowledge needed to successfully complete the learning tasks in this module.
How to Use This Page: Bookmark this page and refer to it throughout Module. You can also access individual snippets directly from the lesson pages when you see the “Review” sections.

Core Next.js App Router Architecture

File-Based Routing System

Understanding Next.js App Router’s file-based routing system is fundamental to migrating from React Router.

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.
Key Concepts You’ll Need:
  • Route segments and special files
  • Dynamic routes with [param] syntax
  • Route groups with (group) syntax
  • Navigation with Link and useRouter

Server and Client Components

The distinction between server and client components is crucial for optimal Next.js performance.

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.
Key Concepts You’ll Need:
  • Server components for data fetching and static content
  • Client components for interactivity and browser APIs
  • The 'use client' directive
  • Component composition patterns

Layouts

Creating consistent UI patterns with layouts.

Overview

Next.js App Router provides powerful layout features for creating consistent UI patterns. Understanding layouts is crucial for building maintainable applications.

What are Layouts?

Layouts are shared UI that persists across route changes and maintains state during navigation.

Key Characteristics

Layout Characteristics

  • Shared UI across multiple pages
  • Maintains state during navigation
  • Renders once and stays mounted
  • Perfect for navigation, headers, footers

Layout Benefits

  • Consistent user experience
  • Performance optimization
  • State preservation
  • Reduced re-rendering

Root Layout

The root layout wraps all pages and is required in every Next.js app.
// app/layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "My App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en" className={inter.className}>
      <body>
        <header>
          <nav>
            <a href="/">Home</a>
            <a href="/about">About</a>
            <a href="/contact">Contact</a>
          </nav>
        </header>
        <main>{children}</main>
        <footer>
          <p>&copy; 2024 My App. All rights reserved.</p>
        </footer>
      </body>
    </html>
  );
}

Nested Layouts

Layouts can be nested to create hierarchical UI structures.
// app/dashboard/layout.tsx
import Sidebar from "@/components/Sidebar";

export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="dashboard-layout">
      <Sidebar />
      <div className="dashboard-content">{children}</div>
    </div>
  );
}

Layout Hierarchy

app/
├── layout.tsx              # Root layout (wraps everything)
├── dashboard/
│   ├── layout.tsx          # Dashboard layout (wraps dashboard pages)
│   ├── page.tsx            # /dashboard
│   └── settings/
│       ├── layout.tsx      # Settings layout (wraps settings pages)
│       └── page.tsx        # /dashboard/settings

Basic Layout Patterns

Simple Layout with Navigation

// app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <div className="min-h-screen flex flex-col">
          <header className="bg-white shadow-sm">
            <nav className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
              <div className="flex justify-between items-center">
                <div className="flex space-x-8">
                  <a href="/" className="text-gray-900">
                    Home
                  </a>
                  <a href="/about" className="text-gray-900">
                    About
                  </a>
                  <a href="/contact" className="text-gray-900">
                    Contact
                  </a>
                </div>
              </div>
            </nav>
          </header>

          <main className="flex-1">{children}</main>

          <footer className="bg-gray-800 text-white">
            <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
              <p>&copy; 2024 My App. All rights reserved.</p>
            </div>
          </footer>
        </div>
      </body>
    </html>
  );
}

Dashboard Layout

// app/dashboard/layout.tsx
import Sidebar from "@/components/Sidebar";
import Header from "@/components/Header";

export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="dashboard-layout">
      <Sidebar />
      <div className="dashboard-main">
        <Header />
        <div className="dashboard-content">{children}</div>
      </div>
    </div>
  );
}

Layout with Metadata

// app/blog/layout.tsx
import { Metadata } from "next";

export const metadata: Metadata = {
  title: "Blog - My App",
  description: "Read our latest blog posts",
};

export default function BlogLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="blog-layout">
      <aside className="blog-sidebar">
        <h2>Categories</h2>
        {/* Category links */}
      </aside>
      <main className="blog-main">{children}</main>
    </div>
  );
}

Best Practices for Module 1

Layout Design

  1. Keep Layouts Simple: Focus on shared UI elements and avoid complex logic
  2. Use Nested Layouts: Create hierarchical layouts for better organization
  3. Optimize Performance: Minimize re-renders and use proper state management
  4. Handle Loading States: Provide appropriate loading states
Key Takeaway: Layouts are powerful tools for creating consistent UI patterns. Use them for shared UI that should persist across routes, like navigation, headers, and footers.
Key Concepts You’ll Need:
  • Root layout for global UI
  • Nested layouts for section-specific UI
  • Layout hierarchy and composition
  • Basic layout patterns

Loading States

Providing better user experience with loading states.

Overview

Next.js App Router provides built-in support for loading states through the loading.tsx file. This feature helps create better user experiences by handling loading states gracefully.

Loading UI

The loading.tsx file creates a loading UI that shows while a route segment is loading.

Key Benefits

Loading Benefits

  • Instant loading feedback
  • Better perceived performance
  • Prevents layout shift
  • Automatic implementation

Loading Scope

  • Applies to route segment
  • Wraps page and children
  • Automatic activation
  • Nested loading support

Basic Loading Component

// app/loading.tsx
export default function Loading() {
  return (
    <div className="flex items-center justify-center min-h-screen">
      <div className="animate-spin rounded-full h-32 w-32 border-b-2 border-blue-500"></div>
    </div>
  );
}

Simple Skeleton Loading

// app/dashboard/loading.tsx
export default function DashboardLoading() {
  return (
    <div className="container mx-auto px-4 py-8">
      <div className="mb-8">
        <div className="h-8 bg-gray-200 rounded w-1/3 animate-pulse"></div>
        <div className="h-4 bg-gray-200 rounded w-1/2 mt-2 animate-pulse"></div>
      </div>

      <div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
        {Array.from({ length: 4 }).map((_, i) => (
          <div key={i} className="bg-white p-6 rounded-lg shadow-md">
            <div className="h-4 bg-gray-200 rounded w-24 mb-2 animate-pulse"></div>
            <div className="h-8 bg-gray-200 rounded w-16 animate-pulse"></div>
          </div>
        ))}
      </div>

      <div className="bg-white rounded-lg shadow-md p-8">
        <div className="space-y-4">
          {Array.from({ length: 5 }).map((_, i) => (
            <div
              key={i}
              className="h-4 bg-gray-200 rounded animate-pulse"></div>
          ))}
        </div>
      </div>
    </div>
  );
}

Nested Loading States

app/
├── loading.tsx              # Root loading
├── dashboard/
│   ├── loading.tsx          # Dashboard loading
│   ├── page.tsx
│   └── settings/
│       ├── loading.tsx      # Settings loading
│       └── page.tsx
Each loading component applies to its route segment and children.

Basic Loading Patterns

Page-Level Loading

// app/blog/loading.tsx
export default function BlogLoading() {
  return (
    <div className="blog-loading">
      <div className="loading-header">
        <div className="h-8 bg-gray-200 rounded w-1/3 animate-pulse"></div>
      </div>

      <div className="loading-content">
        <div className="space-y-4">
          {Array.from({ length: 3 }).map((_, i) => (
            <div
              key={i}
              className="h-32 bg-gray-200 rounded animate-pulse"></div>
          ))}
        </div>
      </div>
    </div>
  );
}

Simple Card Loading

// app/products/loading.tsx
export default function ProductsLoading() {
  return (
    <div className="products-loading">
      <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
        {Array.from({ length: 6 }).map((_, i) => (
          <div key={i} className="bg-white p-6 rounded-lg shadow-md">
            <div className="h-48 bg-gray-200 rounded mb-4 animate-pulse"></div>
            <div className="h-4 bg-gray-200 rounded w-3/4 mb-2 animate-pulse"></div>
            <div className="h-4 bg-gray-200 rounded w-1/2 animate-pulse"></div>
          </div>
        ))}
      </div>
    </div>
  );
}

Best Practices for Module 1

Loading State Design

  1. Match Layout Structure: Create loading states that match your page layout
  2. Use Skeleton Screens: Implement skeleton loading for better perceived performance
  3. Provide Context: Show what’s loading and why it might take time
  4. Keep It Simple: Start with basic loading patterns and enhance later

File Organization

app/
├── loading.tsx              # Global loading
├── dashboard/
│   ├── loading.tsx          # Dashboard-specific loading
│   └── page.tsx
└── blog/
    ├── loading.tsx          # Blog-specific loading
    └── page.tsx
Key Takeaway: Loading states are essential for creating good user experiences. Use loading.tsx files to provide immediate feedback while pages are loading, and match the loading UI to your page structure.
Key Concepts You’ll Need:
  • loading.tsx for loading states
  • Basic skeleton loading patterns
  • Nested loading states
  • Loading state best practices

Additional Resources

For more advanced concepts like streaming, suspense, concurrent features, and error boundaries, refer to the comprehensive snippets in later modules.

VSL Service Center Context

Project Structure Analysis

Before starting the migration tasks, familiarize yourself with the VSL Service Center project structure:
1

Frontend Structure

  • src/Component/ - Main component directory
  • src/Routes/ - React Router configuration
  • src/Component/ui/ - Reusable UI components
  • src/Component/Login/ - Authentication components
2

Key Components to Migrate

  • App.js - Main application component
  • Navbar.js - Navigation component
  • LandingPage.js - Home page component
  • ui/Card.jsx - Reusable card component
3

Routing Structure

  • Current React Router setup
  • Route definitions and navigation
  • Protected routes and authentication

Migration Strategy

Component Migration Order

  1. UI components first (no dependencies)
  2. Layout components second
  3. Business logic components last
  4. Authentication components

Routing Migration

  1. Analyze current route structure
  2. Map to Next.js file-based routing
  3. Implement layouts and templates
  4. Update navigation components

Quick Reference

Essential Commands

# Create new Next.js project
npx create-next-app@latest my-app --typescript --tailwind --eslint --app

# Run development server
npm run dev

# Build for production
npm run build

# Start production server
npm start

Key File Patterns

app/
├── layout.tsx          # Root layout
├── page.tsx            # Home page
├── loading.tsx         # Loading UI
├── error.tsx           # Error boundary
├── not-found.tsx       # 404 page
├── template.tsx        # Template (optional)
└── (auth)/             # Route group
    ├── layout.tsx      # Auth layout
    ├── login/
    │   └── page.tsx    # Login page
    └── register/
        └── page.tsx    # Register page

Component Patterns

// Server Component (default)
export default function ServerComponent() {
  return <div>Server rendered content</div>;
}

// Client Component
("use client");

import { useState } from "react";

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

Troubleshooting Guide

Common Issues and Solutions

Problem: Component not showing up after migration Solution: Check file naming (page.tsx for routes), verify imports, ensure proper component export
Problem: Routes not working as expected Solution: Verify file structure matches desired URL structure, check for typos in folder names
Problem: “use client” directive not working Solution: Ensure directive is at the very top of the file, check for any code before the directive
Problem: Layout styles not showing Solution: Verify layout.tsx is in correct directory, ensure it wraps children properly

Next Steps

After reviewing this supportive information, you’ll be ready to:
  1. Start with Module 1, Lesson 1: Next.js App Router Fundamentals
  2. Set up your development environment with the VSL Service Center project
  3. Begin the migration process following the structured approach outlined in the lessons
Remember: These supportive information snippets are available throughout the module. Look for “Review” callouts in the lesson pages to access specific information when you need it.