CoachnestCoachnest
Sign InGet Started
Back to course

Next.js 16: The Complete Developer Guide

…
—
Contents
1

What's New in Next.js 16

Reading14mFree
2

Installation, CLI & Your First Project

Reading12mFree
3

Project Structure & Conventions Deep Dive

Reading16m
4

Turbopack — The New Default Bundler

Video18m
5

Configuring TypeScript, ESLint & next.config.ts

Reading14m
6

Chapter 1 — Quiz

Quiz10m
7

App Router Fundamentals

Reading16m
8

Dynamic Routes, Catch-Alls & Type-Safe Params

Reading14m
9

Route Groups & Parallel Routes

Reading16m
10

Intercepting Routes & Modal Patterns

Reading12m
11

Loading, Error & Not-Found UI

Reading12m
12

Chapter 2 — Routing Quiz

Quiz12m
13

Understanding the Server/Client Boundary

Reading18m
14

Choosing When to use "use client"

Reading14m
15

Composing Server & Client Components

Reading14m
16

server-only, client-only & Code Splitting

Reading12m
17

Chapter 3 — Quiz

Quiz10m
18

Fetching Data in Server Components

Reading14m
19

The Next.js 16 Cache Model

Reading16m
20

Revalidation: revalidateTag, revalidatePath & On-Demand

Reading12m

Cache Components — Building Reusable Cached Functions

Reading14m
22

Search Params, Cookies & Dynamic APIs

Reading12m
23

Chapter 4 — Quiz

Quiz12m
24

Server Actions From First Principles

Reading16m
25

Forms with useActionState & Progressive Enhancement

Reading14m
26

Optimistic UI with useOptimistic

Reading12m
27

Validation with Zod + Server Actions

Reading12m
28

Chapter 5 — Quiz

Quiz10m
29

Static vs Dynamic vs Streaming

Reading14m
30

generateStaticParams & Pre-Rendering Dynamic Routes

Reading12m
31

Incremental Static Regeneration (ISR)

Reading12m
32

Partial Prerendering (PPR)

Reading16m
33

Edge Runtime vs Node.js Runtime

Reading12m
34

Rendering Strategies Deep Dive

Video22m
35

Chapter 6 — Quiz

Quiz12m
36

Tailwind CSS v4 with Next.js 16

Reading14m
37

CSS Modules, Global Styles & Scoped CSS

Reading10m
38

next/image — Smart, Fast Images

Reading14m
39

Fonts, Icons & Metadata

Reading12m
40

Chapter 7 — Quiz

Quiz10m
41

Middleware Fundamentals

Reading14m
42

Sessions, JWTs & Cookies

Reading16m
43

Protecting Server Components & Server Actions

Reading12m
44

Chapter 8 — Quiz

Quiz10m
45

Prisma with Next.js — The Production Setup

Reading14m
46

Mutations: Server Actions + Database Writes

Reading12m
47

Route Handlers & REST APIs

Reading12m
48

Chapter 9 — Quiz

Quiz10m
49

Unit & Component Testing with Vitest

Reading12m
50

End-to-End Testing with Playwright

Reading14m
51

Deploying to Vercel

Reading12m
52

Self-Hosting with Docker

Reading14m
53

Production Performance Checklist

Reading12m
54

Final Assessment — Next.js 16 Mastery

Quiz20m
←→navigate lessons
Chapter 4 of 10·Chapter 4 — Data Fetching & The Next.js 16 Cache Model
Lesson 21 of 54Reading14 min

Cache Components — Building Reusable Cached Functions

#Cache Components — Building Reusable Cached Functions¶

The pattern: wrap each domain operation in a cached function. Pages and components call those functions; the cache layer is invisible.

A Domain Module¶

ts
34 lines
1// lib/data/courses.ts
2"use cache";
3import { cacheTag, cacheLife } from "next/cache";
4import { db } from "@/lib/db";
5import "server-only";
6
7export async function getCourse(slug: string) {
8  cacheTag("course", `course:${slug}`);
9  cacheLife("hours");
10  return db.course.findUnique({
11    where: { slug },
12    include: { sections: { include: { lessons: true } } },
13  });
14}
15
16export async function getFeaturedCourses() {
17  cacheTag("course");
18  cacheLife("hours");
19  return db.course.findMany({
20    where: { status: "PUBLISHED" },
21    orderBy: { createdAt: "desc" },
22    take: 6,
23  });
24}
25
26export async function getReviews(courseId: string) {
27  cacheTag(`reviews:${courseId}`);
28  cacheLife("minutes");
29  return db.review.findMany({
30    where: { courseId },
31    include: { user: { select: { name: true, avatar: true } } },
32    orderBy: { createdAt: "desc" },
33  });
34}

A Mutation Module¶

ts
17 lines
1// lib/data/mutations.ts
2"use server";
3import { revalidateTag } from "next/cache";
4import { db } from "@/lib/db";
5
6export async function publishCourse(id: string) {
7  const course = await db.course.update({ where: { id }, data: { status: "PUBLISHED" } });
8  revalidateTag("course");
9  revalidateTag(`course:${course.slug}`);
10  return course;
11}
12
13export async function postReview(courseId: string, input: { rating: number; comment: string }) {
14  const review = await db.review.create({ data: { courseId, ...input } });
15  revalidateTag(`reviews:${courseId}`);
16  return review;
17}

Usage¶

tsx
13 lines
1// app/courses/[slug]/page.tsx
2import { getCourse, getReviews } from "@/lib/data/courses";
3
4export default async function CoursePage({ params }: { params: Promise<{ slug: string }> }) {
5  const { slug } = await params;
6  const [course, reviews] = await Promise.all([
7    getCourse(slug),
8    getReviews(slug),
9  ]);
10
11  if (!course) notFound();
12  return <CourseLayout course={course} reviews={reviews} />;
13}

The page is plain code — no cache wiring visible. All caching lives in the data layer.

Composition¶

A cached function can call another cached function. Tags merge:

ts
10 lines
1"use cache";
2export async function getCourseWithStats(slug: string) {
3  cacheTag(`course:${slug}`);
4  cacheLife("hours");
5
6  const course = await getCourse(slug);                  // also cached
7  const reviews = await getReviews(course!.id);          // also cached
8  const stats = computeStats(reviews);
9  return { ...course, stats };
10}

Invalidating course:${slug} wipes both this composed function and its sub-calls.

When NOT to Cache¶

CaseReason
Authenticated user dataDifferent per user; cache pollution risk
Real-time data (prices, scores)Expected to be fresh
Personalized recommendationsSame as above
Cart contentsPer-session, ephemeral
Anything with PII in argsTag explosion + privacy concerns

For these, just call the DB directly — no "use cache".

Designing Tag Hierarchies¶

A small, deliberate tag scheme makes invalidation trivial:

course ← the whole "courses" namespace course:<slug> ← one specific course reviews:<courseId> ← reviews for one course user:<id>:enrollments ← a user's enrollment list

When in doubt, tag more, not less — but avoid PII (emails, IPs) in tag names; they're stored verbatim.

Previous

Revalidation: revalidateTag, revalidatePath & On-Demand

Next

Search Params, Cookies & Dynamic APIs

Use ← → arrow keys to navigate between lessons