Clarifying Concepts
CSR
Client-Side Rendering.
Core definition: the main content of a page is rendered by JavaScript executed in the browser. Data sources can be:
- fetch;
- data hardcoded in JS;
- other similar approaches;
CSR = the main UI content of HTML depends on browser JS execution to be produced.
bundle
Original meaning: a bundle, a bale, a package of things. In frontend engineering, it can be understood as: a group of JavaScript code/resources packaged for the browser after being processed by a build tool.
For example:
import React from 'react'
import Button from './Button'
import { formatDate } from './utils'These pieces of code are scattered across:
page.tsx
Button.tsx
utils.ts
React
third-party dependencies
...The build tool analyzes the dependency relationships between these modules:
source code modules
β
dependency graph
β
compile / transform / optimize / split
β
one or more JS filesThe code ultimately delivered to the browser is typically referred to as: JavaScript bundle, client bundle, bundle size, etc.
Next.js doesn't crudely stuff everything into a single bundle.js; in practice, it performs code splitting/chunking, producing multiple chunks.
Therefore, bundle can be understood as: the packaged collection of client-side code.
prerender
pre + render
beforehand + renderRendering the page ahead of time, before the user actually visits it. This is a general concept in Web / SSR / SSG.
Hydration
hydrate itself means: to supply water to something.
Imagine the HTML obtained from build as a: dry page that already has: tags, text, structure, content, but lacks React client-side behavior.
After the browser loads JS, React performs hydration on the existing HTML: rather than tearing down and rebuilding the page, it injects/restores client-side capabilities on top of the existing HTML.
During hydration, the result of the client's first render does need to match the HTML obtained from server-side/pre-rendering.
Therefore, translated as: hydration, it is the core mechanism in React's SSR/pre-rendering system. React takes over the existing HTML in the browser and turns it into an interactive React application.
Existing HTML
<button>0</button>
β
β Download Client JS
β
React executes
β
β
Hydration
β
βββ Build React component tree
βββ Map React to existing DOM
βββ Set up event handlers
βββ Restore interactivity
β
button is clickable
Understanding
The most straightforward understanding: declaring a Client Component boundary.
- This is distinct from "Client Component" and "CSR pure client-side rendering" β they are not the same thing.
- It indicates that this component needs client-side JavaScript capabilities; it does NOT mean this component can only generate HTML on the client.
How to understand this?
With static export, Client Components are also pre-rendered during the next build phase; static HTML for the initial visit is still produced.
'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(count + 1)}>
{count}
</button>
)
}Here, 'use client' essentially tells Next.js: starting from this file, establish a Server / Client boundary; this module and its client dependencies need to be included in the browser JavaScript bundle.
- Next.js
page.tsxandlayout.tsxare Server Components by default. You should only establish a'use client'boundary when you need client-side capabilities.
For example:
page.tsx Server Component
β
βββ Article.tsx Server Component
β
βββ LikeButton.tsx Client Component
β
'use client'The general flow is as follows:
next build
β
βββ Server Components execute
β
βββ Client Components also participate in pre-rendering
β
β
Generate static HTML
+
Generate JS for Client Components
β
Browser
β
βββ Display HTML first
βββ JS hydration
β
onClick / state / effect take effectYou need a Client Component in the following cases:
useStateuseEffect- event handlers, e.g.
onClick,onChange - browser APIs, e.g.
window,document,localStorage,Geolocation - custom hooks that depend on these capabilities
Any UI that depends on browser runtime state, lifecycle, events, or Browser APIs generally needs to be inside a Client Component boundary.
'use client'
function Counter() {
const [count, setCount] = useState(0)
useEffect(() => {
console.log('browser')
}, [])
return (
<button onClick={() => setCount(count + 1)}>
{count}
</button>
)
}Broken down:
BUILD
ββββββββββββββββ
Counter render
β
<button>0</button>
β
Write to HTML
BROWSER
ββββββββββββββββ
HTML displayed immediately
β
Download Counter JS
β
React hydration
β
useEffect executes
β
onClick starts working
β
useState can changeSEO, first-screen content, and performance are not determined by use client, but by: whether the core content is already in the HTML at build time.
'use client'
function Article() {
return (
<article>
<h1>TypeScript Basics</h1>
<p>This is an articleβ¦</p>
</article>
)
}This will still be pre-rendered into HTML. The HTML that search engines receive may itself contain:
<h1>TypeScript Basics</h1>
<p>This is an articleβ¦</p>What you need to watch out for is code like this:
'use client'
function Article() {
const [content, setContent] = useState('')
useEffect(() => {
fetch('/article.json')
.then(...)
.then(setContent)
}, [])
return <article>{content}</article>
}At build time, this may only produce:
<article></article>After the browser executes JS, it becomes:
<article>
Lots of contentβ¦
</article> Next.js App Router
Component
β
βββββββββββββ΄ββββββββββββ
β β
Server Component Client Component
default 'use client'
β β
build/server execution needs browser JS
β β
βββββββββββββ¬ββββββββββββ
β
prerender
β
HTML
β
Browser loads
β
Client JS hydration
β
Interactive