Course
Server vs. Client Component
Next.js Mastery: From Fundamentals to Full-Stack
Unlock the power of Next.js with this comprehensive course! Starting with the basics, you’ll learn essential skills such as routing, data fetching, and styling. Progress through practical projects, including building your own React Notes app, to gain hands-on experience. Dive into the source code to understand the inner workings and core principles of Next.js. Perfect for developers with a basic knowledge of React and Node.js, this course ensures you’ll be ready to create high-performance full-stack applications efficiently. Join us and master Next.js, backed by industry experts and a supportive learning community.
Server Components vs. Client Components
How to Choose?
Rendering Environment
Server components are only rendered on the server, while client components are rendered on the server once and then on the client.
What does this mean? Let's write an example:
Create
app/client/page.js
:'use client'
import { useState } from 'react';
console.log('client')
export default function Page() { console.log('client Page')
const [text, setText] = useState('init text');
return ( <button onClick={() => { setText('change text') }}>{text}</button> )}
Create
app/server/page.js
:javascriptCopy codeconsole.log('server')
export default function Page() { console.log('server Page')
return ( <button>button</button> )}
Now, when you run
npm run build
, what will be printed?The answer is that both client components and server components will print:
And based on the output, both
/client
and /server
use static rendering.When running
npm run start
, what will be printed?The answer is nothing will be printed in the command line. When accessing
/client
, the browser will print:client page-4b10cfcd1c67a6d6.js:1
client Page page-4b10cfcdic67a6d6.js:1
When accessing
/server
, the browser will not print anything:
The client component prints in the browser because it runs on the client side. But why does the client component also run once during the build? Let's check the
/client
response:
You will see that
init text
comes from the useState
value, but it still appears in the HTML. This is the purpose of compiling the client component, to quickly display content on the initial load.So, server components and client components don't correspond directly to physical servers and clients. Server components run at build time and on the server, while client components run at build time, on the server (to generate initial HTML), and on the client (to manage the DOM).
Alternating Between Server and Client Components
In actual development, it's unlikely to use purely server or client components. When alternating between them, remember:
Server components can directly import client components, but client components cannot import server components.
'use client'
// This is not allowedimport ServerComponent from './Server-Component'
export default function ClientComponent({ children }) { const [count, setCount] = useState(0)
return ( <> <button onClick={() => setCount(count + 1)}>{count}</button> <ServerComponent /> </> )}
As mentioned when introducing client components:
"use client"
declares the boundary between server and client component modules.
When defined in a file, all imported modules, including child components, are considered part of the client bundle.
The default is server components, but when imported into client components, they are considered client components. The rule that client components cannot import server components is to ensure that if you use Node APIs in a server component, it won't be imported into a client component.
However, you can pass server components as props to client components:
'use client'
import { useState } from 'react'
export default function ClientComponent({ children }) { const [count, setCount] = useState(0)
return ( <> <button onClick={() => setCount(count + 1)}>{count}</button> {children} </> )}
import ClientComponent from './client-component'import ServerComponent from './server-component'
export default function Page() { return ( <ClientComponent> <ServerComponent /> </ClientComponent> )}
This way,
<ClientComponent>
and <ServerComponent>
are decoupled and rendered independently.Note: You might wonder why you should bother usingServerComponent
. This is becauseServerComponent
has many benefits, such as not being bundled into the client bundle. Why is it okay to pass it as props? We will explain this more concretely in the practical project "React Notes | Note Search".
Component Rendering Principles
On the server side:
Next.js uses React APIs to orchestrate rendering. The rendering work is divided into chunks based on routes and Suspense, and each chunk is rendered in two steps:
- React renders server components into a special data format called React Server Component Payload (RSC Payload).
- Next.js uses the RSC Payload and client component code to render HTML on the server.
The RSC Payload contains the following information:
- The rendered results of server components.
- Placeholders and reference files for client components.
- Data passed from server components to client components.
On the client side:
- Load the rendered HTML to quickly display a non-interactive UI.
- The RSC Payload is used to reconcile the client and server component trees and update the DOM.
- JavaScript code is used to hydrate client components, making the application interactive.
Note: The diagram above describes the initial page load process. SC represents Server Components, and CC represents Client Components.
In the previous section, we discussed that Suspense and Streaming still have some issues, such as the need to load the same amount of JavaScript and the requirement to hydrate all components, even those that don't need hydration.
Using server components and client components can solve these problems. Server component code is not bundled into the client bundle. When rendering, only client components need to be hydrated, not server components.
During subsequent navigation:
- Client components are fully rendered on the client.
- React uses the RSC Payload to reconcile the client and server component trees and update the DOM.