Skip to content

Next.js

core

Assumes you have read: Rendering strategies, TypeScript's Type System

Next.js uses React’s server/client boundary to keep data fetching and non-interactive rendering on the server, while Client Components carry browser behavior. The timeline and streaming strategies are prerequisites; this page focuses on the boundary’s cost.

// Server Component by default
export default async function Page() {
const data = await getData();
return <ClientFilter data={data} />;
}

'use client' marks a module boundary; its imports join the client graph and its props must be serializable. Checked against Next.js 16.3.1 documentation, 2026-08-17: Server and Client Components.

Moving a component to the client adds its code and reachable imports to browser bytes, plus serialization and hydration work for its rendered boundary. Keeping it server-side removes that client code but can require a request/stream boundary for data. For m client modules, startup work is approximately transfer + parse + execute of their reachable graph, then hydration of interactive instances; server rendering adds server compute per request unless cached. Measure route chunks and device TTI, not the framework’s headline bundle.

Do not use Next.js for a static page that needs no React runtime unless its platform benefits justify the dependency. Do not mark a whole page 'use client' to fix one interactive control; keep the boundary narrow. Choose a simpler static or server-first tool when the app has no need for Next’s routing, caching, or React integration.

Next.js fits React products needing a mix of server data access, indexed HTML, streaming, and browser interactions. Teams often keep page shells and data access server-side and isolate filters, editors, and charts as client boundaries.

A small button ships megabytes. The symptom is a large route chunk; a broad 'use client' boundary pulled a chart, data client, or utility graph into the browser. Split the boundary and inspect the production analyzer. The page shows stale data. The cause is an incorrect cache/revalidation assumption; declare and test freshness for the route. Hydration is slow or mismatches. Too many client instances or nondeterministic output inflate work; narrow boundaries and make server/client initial output equal.

  1. A page needs one date-picker but is entirely client-marked. Solution: move the marker to the picker and pass serializable data; compare route chunk bytes.
  2. A dashboard must be fresh per request. Solution: choose an explicit dynamic/fetch policy and verify response behavior under two requests; do not infer freshness from local development.

Next.js lets React render non-interactive work on the server and ships browser code only across Client Component boundaries. The caveat is that the boundary is an accounting boundary: imports, serialization, hydration, and cache policy all affect the real result.