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

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
21

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 3 of 10·Chapter 3 — Server Components & Client Components
Lesson 16 of 54Reading12 min

server-only, client-only & Code Splitting

#server-only, client-only & Code Splitting¶

The Problem¶

You write a helper:

ts
8 lines
1// lib/payments.ts
2import Stripe from "stripe";
3
4export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
5
6export async function createCheckout(items: Item[]) {
7  return stripe.checkout.sessions.create({ /* … */ });
8}

A teammate imports it into a Client Component. Now the Stripe secret key is referenced (and potentially logged) in client code, and the entire stripe Node SDK gets pulled into the browser bundle.

The Fix: server-only¶

Add an import that fails at build time if the file is ever included in a client bundle:

ts
2 lines
1import "server-only";   // ← this line is enough
2import Stripe from "stripe";

The build fails with:

Error: This module is marked with "server-only" and cannot be imported from a Client Component.

Equivalent on the other side:

ts
2 lines
1import "client-only";
2import confetti from "canvas-confetti";

Use these on every sensitive boundary.

Dynamic Imports for Heavy Client Libraries¶

A Chart component that pulls in 200 KB of charting code should be loaded only when needed:

tsx
7 lines
1"use client";
2import dynamic from "next/dynamic";
3
4const Chart = dynamic(() => import("./Chart"), {
5  ssr: false,                       // skip SSR (e.g., for canvas/three.js)
6  loading: () => <ChartSkeleton />,
7});

ssr: false cannot be used in a Server Component. It's a client-side dynamic import.

React.lazy + Suspense (Server Components)¶

tsx
10 lines
1import { lazy, Suspense } from "react";
2const Mdx = lazy(() => import("./Mdx"));
3
4export default function Doc() {
5  return (
6    <Suspense fallback={<p>Loading…</p>}>
7      <Mdx />
8    </Suspense>
9  );
10}

Per-Route Code Splitting (Automatic)¶

Each route segment is automatically code-split — you don't need to think about it. Importing HeavyComponent only in app/admin/page.tsx keeps it out of /dashboard's bundle.

Auditing What's in the Client Bundle¶

bash
1 line
1npx @next/bundle-analyzer

Or use the built-in analyzer:

ts
8 lines
1// next.config.ts
2import withBundleAnalyzer from "@next/bundle-analyzer";
3
4const enable = process.env.ANALYZE === "true";
5
6export default withBundleAnalyzer({ enabled: enable })({
7  // …your config
8});
bash
1 line
1ANALYZE=true npm run build

A Rule of Thumb for Client JS¶

Aim for < 100 KB of JavaScript on a typical page (gzip). Above 150 KB, hydration starts to noticeably delay interactivity on mid-range mobile.

The biggest wins come from:

  1. 1Pushing more rendering to the server
  2. 2Lazy-loading heavy client libraries
  3. 3Avoiding moment.js, lodash, axios in favor of native APIs and date-fns

Previous

Composing Server & Client Components

Next

Chapter 3 — Quiz

Use ← → arrow keys to navigate between lessons