Use Server Components by default and introduce Client Components only where browser interactivity requires them.
Course navigationPhase 03⌄
Next.js Fundamentals
Build the Next.js application shell while understanding App Router routing, layouts, Server Components, Client Components, environment boundaries, and rendering behavior.
Phase outcome and project increment
Learning outcome
Build the Next.js application shell while understanding App Router routing, layouts, Server Components, Client Components, environment boundaries, and rendering behavior.
Concrete deliverable
A navigable TaskFlow web shell with route groups/layouts, server-first composition, selective client interactivity, and verified environment handling.
Principles to keep while coding
Treat the App Router file system as application structure, not as a place to hide business rules.
Keep secrets and privileged data access on the server side of the Next.js boundary.
Understand caching and rendering behavior before trying to fix stale UI with ad-hoc client state.
Guided phase lab
Do these in order. Each step should end with evidence before the next one starts.
- 01
Generate the web app in the monorepo and verify the development server from the correct workspace.
- 02
Create nested routes and a shared layout, then predict which layouts persist across navigation.
- 03
Build one component as a Server Component and identify exactly why it can remain server-only.
- 04
Add one Client Component for interaction and explain what 'use client' changes in the dependency graph.
- 05
Add server-only and public environment values and prove which can reach the browser bundle.
- 06
Use browser DevTools and terminal logs to distinguish server rendering from client execution.
pnpm --filter web dev
pnpm --filter web buildPredict what each command proves before running it. A command is useful only when its output answers a question.
Failures you should know how to diagnose
Adding 'use client' too high in the tree
Predict the symptom, capture evidence at the nearest boundary, change one variable, and verify the same reproduction after the fix.
Accessing browser APIs from Server Components
Predict the symptom, capture evidence at the nearest boundary, change one variable, and verify the same reproduction after the fix.
Leaking secrets through NEXT_PUBLIC variables
Predict the symptom, capture evidence at the nearest boundary, change one variable, and verify the same reproduction after the fix.
Using effects for work that belongs in server rendering or a data layer
Predict the symptom, capture evidence at the nearest boundary, change one variable, and verify the same reproduction after the fix.
Lessons
The source chapters come first. Every phase now ends with a guided build, a debugging workshop, and an independent challenge so reading turns into transferable skill.
Create the TaskFlow Next.js App
App Router Mental Model
Server and Client Components
Routing Loading and Errors
Styling Assets and Accessibility
Environment DevTools and Debugging
Build the TaskFlow Frontend Shell
Exercises and Phase Exit
Guided Build Lab
Purpose Build the Next.js application shell while understanding App Router routing, layouts, Server Components, Client Components, environment boundaries, and rendering behavior.
Debugging Workshop
Goal Learn to diagnose this phase's failures systematically instead of reinstalling packages, changing random configuration, or copying a fix without understanding it.
Phase Synthesis & Independent Challenge
Mission Rebuild the capability of Phase 03 without following the exact code line-by-line. The goal is transfer: can you use the ideas when the variable names, UI, or route shape changes?
Mastery Capstone Lab
Integrate Phase 03 without following a transcript.
Exercises that increase independence
Generate the web app in the monorepo and verify the development server from the correct workspace.
Build one component as a Server Component and identify exactly why it can remain server-only.
reproduce and diagnose — Adding 'use client' too high in the tree.
deliver A navigable TaskFlow web shell with route groups/layouts, server-first composition, selective client interactivity, and verified environment handling.
Can choose server vs client intentionally and Can explain layout/page routing.
Do not leave the phase only because you reached the bottom
Phase exit criteria
Scenario and concept quiz
These questions test engineering choices rather than whether you remember a lesson title.
Reference files
Attempt the work first. Use snapshots to compare a checkpoint, not as a substitute for understanding.
reference/TaskFlow_Web_Snapshot/package.json
{
"name": "web",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint"
},
"dependencies": {
"next": "16.3.3",
"react": "19.3.0",
"react-dom": "19.3.0"
}
}reference/TaskFlow_Web_Snapshot/src/app/globals.css
:root {
--background: #f6f8fa;
--surface: #ffffff;
--text: #17212b;
--border: #d8e0e6;
}
* { box-sizing: border-box; }
body {
margin: 0;
background: var(--background);
color: var(--text);
font-family: Arial, sans-serif;
}
main {
width: min(1100px, calc(100% - 2rem));
margin: 0 auto;
padding: 2rem 0;reference/TaskFlow_Web_Snapshot/src/app/layout.tsx
import type { Metadata } from "next";
import Link from "next/link";
import "./globals.css";
export const metadata: Metadata = {
title: "TaskFlow",
description: "Project and task management for focused teams",
};
export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="en">
<body>
<header>
<Link href="/">TaskFlow</Link>
<nav aria-label="Primary navigation">
<Link href="/projects">Projects</Link>
<Link href="/settings">Settings</Link>
</nav>
</header>reference/TaskFlow_Web_Snapshot/src/app/page.tsx
export default function HomePage() {
return (
<section>
<p>TaskFlow</p>
<h1>Keep work visible and moving.</h1>
<p>Projects, tasks, ownership, and progress will grow here throughout the course.</p>
</section>
);
}reference/TaskFlow_Web_Snapshot/src/app/projects/page.tsx
const projects = [
{ id: 1, name: "Website refresh", status: "Active" },
{ id: 2, name: "Inventory rollout", status: "Planning" },
];
export default function ProjectsPage() {
return (
<section>
<h1>Projects</h1>
{projects.map((project) => (
<article key={project.id}>
<h2>{project.name}</h2>
<p>{project.status}</p>
</article>
))}
</section>
);
}reference/TaskFlow_Web_Snapshot/src/app/settings/density-toggle.tsx
"use client";
import { useState } from "react";
export function DensityToggle() {
const [compact, setCompact] = useState(false);
return (
<button onClick={() => setCompact((value) => !value)}>
Density: {compact ? "Compact" : "Comfortable"}
</button>
);
}reference/TaskFlow_Web_Snapshot/src/app/settings/page.tsx
import { DensityToggle } from "./density-toggle";
export default function SettingsPage() {
return (
<section>
<h1>Settings</h1>
<DensityToggle />
</section>
);
}