Unit 1: Fundamentals of Next.js and Routing

INT257 — Modern Web Application Development 9 min read

I. Next.js Orientation

Next.js is a React framework created by Vercel (first released in 2016) for building full-stack web applications. It adds routing, rendering strategies, server capabilities, optimization, and production tooling to React while retaining React’s component-based programming model.

A. Introduction to Next.js

Next.js provides an application framework around React so that developers can focus on features rather than configuring routing, bundling, rendering, and optimization independently.

  • Framework role: React supplies components and state management, while Next.js defines application structure, routing conventions, rendering behavior, and deployment workflows.
  • Full-stack capability: A Next.js project can contain user-interface components, server-rendered pages, route handlers, data-access logic, and middleware in one codebase.
  • Rendering model: Pages may be rendered on the server, generated during a build, streamed incrementally, or updated in the browser.
  • Convention over configuration: Files such as page.tsx, layout.tsx, and error.tsx have framework-defined purposes.
  • Current architecture: Modern Next.js applications normally use the App Router, introduced in Next.js 13 and built around React Server Components.

II. Framework Capabilities

A. Features of Next.js

Next.js combines React development with built-in capabilities needed for scalable, production-oriented web applications.

  • Multiple rendering strategies:
    • Static rendering: HTML is produced ahead of requests and may be cached.
    • Dynamic rendering: HTML is produced at request time when request-specific data is required.
    • Client rendering: Interactive components can fetch and update data in the browser.
  • React Server Components: Components are server components by default in the app directory, reducing the JavaScript sent to the browser.
  • Streaming: Server-rendered content can be delivered progressively through React Suspense and loading.tsx.
  • Data fetching: Server components can use fetch, databases, or server-side services directly.
  • Optimization: next/image, next/font, and next/script optimize images, fonts, and third-party scripts.
  • Route handlers: Files such as app/api/users/route.ts can implement HTTP methods including GET, POST, and DELETE.
  • TypeScript support: TypeScript configuration and route-aware development are integrated into the framework.
  • Production tooling: Next.js provides code splitting, compilation, development diagnostics, and deployment-ready builds.

III. Application Organization

A. Project Structure

A Next.js project separates routes, reusable code, static assets, and configuration through recognizable directories and files.

TEXT
my-app/
├── app/
│   ├── layout.tsx
│   ├── page.tsx
│   └── products/
│       └── page.tsx
├── components/
├── public/
├── next.config.ts
├── package.json
└── tsconfig.json
  • app/ directory: Contains route segments, pages, layouts, route handlers, and route-specific UI boundaries.
  • app/page.tsx: Defines the page served at /.
  • app/layout.tsx: Defines the root layout and normally contains the <html> and <body> elements.
  • components/ directory: Commonly stores reusable components, although its name and location are conventions rather than requirements.
  • public/ directory: Stores static files exposed from the site root; public/logo.png is requested as /logo.png.
  • Configuration files: next.config.ts, tsconfig.json, and eslint.config.mjs control framework, TypeScript, and linting behavior.
  • Colocation: Components and utilities may be placed near the route that uses them because a folder becomes publicly routable only when it contains a routing convention such as page.tsx or route.ts.

IV. Routing Architecture

A. App Router

The App Router is the directory-based routing system under app/, using React Server Components, nested layouts, and special files.

  • Route segments: Each folder inside app represents one URL segment; app/store/cart/page.tsx maps to /store/cart.
  • Page convention: A route becomes accessible when its segment contains page.js, page.jsx, page.ts, or page.tsx.
  • Server default: Pages and layouts are server components unless the file begins with the "use client" directive.
  • Client boundary: Client components are required for browser event handlers, state, effects, and browser-only APIs.
TSX
"use client";

import { useState } from "react";

export default function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
  • Special files: layout.tsx, loading.tsx, error.tsx, not-found.tsx, and route.ts activate specific framework behavior.
  • Server access: A server component can securely call a database or use environment variables without exposing that code in the browser bundle.

V. Local Development

A. Development Workflow

The Next.js workflow moves from project creation and local development to validation, production building, and deployment.

  • Project creation: create-next-app generates the project and can configure TypeScript, ESLint, the App Router, and import aliases.
BASH
npx create-next-app@latest my-app
cd my-app
npm run dev
  • Development server: npm run dev usually serves the application at http://localhost:3000.
  • Fast Refresh: Saved component changes are reflected in the browser while preserving state when possible.
  • Implementation cycle: Developers create route folders, add special files, compose components, and test navigation and server behavior.
  • Quality checks: Type checking and configured linting should be run before producing a release.
  • Production commands:
BASH
npm run build
npm start
  • Build distinction: npm run build creates an optimized production output; npm start serves that output and therefore requires a successful build first.
  • Environment variables: Server-only values remain private by default, while names beginning with NEXT_PUBLIC_ may be included in browser code.

VI. URL Mapping

A. File-based Routing

File-based routing derives application URLs from the directory hierarchy instead of requiring a separate route table.

  • Root mapping: app/page.tsx maps to /.
  • Segment mapping: app/about/page.tsx maps to /about.
  • Index behavior: The page.tsx file supplies the user-facing content for the URL represented by its containing directory.
  • Non-routable files: app/about/team-card.tsx does not create /about/team-card; it is an ordinary colocated module.
  • Route groups: Parenthesized folders organize routes without entering the URL. For example, app/(marketing)/about/page.tsx still maps to /about.
  • Private folders: A name beginning with an underscore, such as _components, explicitly opts that folder out of routing.
  • Conflict rule: Two files must not resolve to the same public URL, even when route groups give them different filesystem paths.

VII. Parameterized Pages

A. Dynamic Routes

Dynamic routes use bracketed segment names when a URL portion is a variable rather than a fixed literal.

  • Single parameter: app/products/[id]/page.tsx matches /products/42, with id equal to "42".
  • Parameter access: App Router pages receive route values through the params prop.
TSX
export default async function ProductPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  return <h1>Product {id}</h1>;
}
  • Catch-all segment: [...slug] captures one or more segments; /docs/web/routing produces a slug array such as ["web", "routing"].
  • Optional catch-all: [[...slug]] also matches the parent path where no captured segment exists.
  • Static generation: generateStaticParams can provide known parameter values so selected dynamic pages are generated during the build.
  • Validation requirement: URL parameters are untrusted strings and must be validated before database queries or security-sensitive operations.

VIII. Hierarchical Routing

A. Nested Routes

Nested routes represent hierarchical URLs by nesting route folders inside one another.

  • Directory hierarchy: app/dashboard/settings/page.tsx maps to /dashboard/settings.
  • Segment relationship: dashboard is the parent segment and settings is its child segment.
  • Shared interface: A layout inside app/dashboard wraps both /dashboard and descendant routes such as /dashboard/settings.
  • Independent pages: app/dashboard/page.tsx and app/dashboard/settings/page.tsx define separate route endpoints.
  • Deep composition: Each nested level may contribute its own layout, loading state, error boundary, and metadata.
  • Design use: Nested routes suit structures such as /courses/[courseId]/lessons/[lessonId], where each segment expresses a resource relationship.

IX. Shared Route Interfaces

A. Layouts

Layouts provide persistent UI shared by multiple pages and nested route segments.

  • Root requirement: The root app/layout.tsx is required and defines the document shell.
TSX
export default function RootLayout({
  children,
}: Readonly<{ children: React.ReactNode }>) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}
  • children prop: The framework inserts the active page or child layout where {children} appears.
  • Nested layout: app/dashboard/layout.tsx can add a dashboard sidebar without affecting unrelated routes.
  • Persistence: Layouts preserve component state and generally do not rerender during navigation between their descendant pages.
  • Composition order: A page is wrapped by its nearest layout, then each ancestor layout, ending with the root layout.
  • Template distinction: A template.tsx resembles a layout but creates a new component instance during navigation, resetting child state and effects.

X. Client Transitions

A. Navigation

Next.js navigation connects routes through optimized links or programmatic router operations.

  • Declarative navigation: The Link component is the standard choice for clickable internal navigation.
TSX
import Link from "next/link";

export default function Menu() {
  return <Link href="/products">Products</Link>;
}
  • Client-side transition: Link updates the displayed route without performing a traditional full-page reload.
  • Prefetching: Production builds may preload linked routes as links enter the viewport, improving perceived speed.
  • Programmatic navigation: Client components can use useRouter from next/navigation.
  • Router operations: router.push("/account") adds a history entry, while router.replace("/login") replaces the current entry.
  • Route information: usePathname reads the current pathname, and useSearchParams accesses query-string values.
  • Accessibility: Navigation must use meaningful link text and semantic links for destinations; buttons should be reserved for actions.

XI. Route State Boundaries

A. Error and Loading UI

Next.js uses route-level special files to display pending states and recover from rendering failures.

  1. Loading UI
    • Convention: loading.tsx supplies immediate fallback content while a route segment streams.
    • Implementation: The framework places the segment content behind a React Suspense boundary.
    • Design principle: A loading skeleton should preserve the approximate dimensions of the arriving content to limit layout movement.
TSX
export default function Loading() {
  return <p>Loading products...</p>;
}
  1. Error UI
    • Convention: error.tsx catches runtime rendering errors in its route segment and descendants.
    • Client requirement: Error boundaries must begin with "use client" because they expose interactive recovery behavior.
    • Recovery function: The reset callback attempts to render the failed segment again.
TSX
"use client";

export default function Error({
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return <button onClick={() => reset()}>Try again</button>;
}
  • Boundary scope: An error in a segment is handled by the nearest parent error boundary; an error.tsx file does not catch errors thrown by the layout at the same segment level.
  • Separation of states: Loading UI represents unfinished work, error UI represents failed work, and not-found.tsx represents unavailable content or a call to notFound().