Course
Server & 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.
Introduction
Server components and client components are crucial concepts in Next.js. Without a detailed understanding, one might simply think that server components are equivalent to SSR (Server-Side Rendering) and client components to CSR (Client-Side Rendering), with server components rendered on the server and client components on the client. However, this is not entirely accurate. In this article, we will delve into Next.js's dual-component model.
Server Components
Introduction
In Next.js, components are server components by default.
For example, create
app/todo/page.js
with the following code:export default async function Page() { const res = await fetch('https://jsonplaceholder.typicode.com/todos'); const data = (await res.json()).slice(0, 10); console.log(data); return ( <ul> {data.map(({ title, id }) => ( <li key={id}>{title}</li> ))} </ul> );}
The request will be executed on the server, and the rendered HTML will be sent to the client:
Since the execution is on the server, the
console.log
output will appear in the command line, not in the client's browser.Advantages
Using server-side rendering has many benefits:
- Data Fetching: Server environments (network, performance, etc.) are usually better and closer to the data source, making data fetching faster. This reduces data loading times and the number of client requests, improving performance.
- Security: Sensitive data and logic remain on the server, avoiding exposure to the client.
- Caching: The results of server-side rendering can be reused in subsequent requests, enhancing performance.
- Bundle Size: Server component code is not bundled into the client bundle, reducing the bundle size.
- Initial Page Load and FCP: Server-side rendering generates HTML for a quick UI display.
- Streaming: Server components can split rendering work into chunks and stream them to the client when ready. This allows users to see parts of the page earlier, without waiting for the entire page to render.
Given these advantages, use server components whenever possible in actual project development.
Limitations
Despite the many benefits, server components have limitations. For example, they cannot use
useState
to manage state or use browser APIs. If you try to use these in Next.js, an error will occur. For example, modifying the code as follows:import { useState } from 'react';
export default async function Page() { const [title, setTitle] = useState('');
const res = await fetch('https://jsonplaceholder.typicode.com/todos'); const data = (await res.json()).slice(0, 10); console.log(data); return ( <ul> {data.map(({ title, id }) => ( <li key={id}>{title}</li> ))} </ul> );}
The browser will show an error:
The error indicates that a client component is needed. So, how do we use client components?
Client Components
Introduction
To use client components, you need to add a
"use client"
directive at the top of the file. Modify app/todo/page.js
as follows:'use client';
import { useEffect, useState } from 'react';
function getRandomInt(min, max) { const minCeiled = Math.ceil(min); const maxFloored = Math.floor(max); return Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled);}
export default function Page() { const [list, setList] = useState([]);
const fetchData = async () => { const res = await fetch('https://jsonplaceholder.typicode.com/todos'); const data = (await res.json()).slice(0, getRandomInt(1, 10)); setList(data); };
useEffect(() => { fetchData(); }, []);
return ( <> <ul> {list.map(({ title, id }) => ( <li key={id}>{title}</li> ))} </ul> <button onClick={() => { location.reload(); }} > Reload </button> </> );}
In this example, we use
useEffect
, useState
, and other React APIs, add a click event to the button, and use browser APIs. Each requires the component to be declared as a client component.Note: "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.
Advantages
- Interactivity: Client components can use state, effects, and event listeners, allowing user interaction.
- Browser APIs: Client components can use browser APIs such as geolocation and localStorage.
Summary
In this article, we introduced and compared server and client components in Next.js. Server components are the default and offer many benefits, such as better performance, security, and smaller bundle sizes. However, they come with limitations, such as not being able to use browser APIs or manage state. Client components, declared with
"use client"
, overcome these limitations and allow for interactivity and the use of browser APIs.Understanding when and how to use server and client components effectively is crucial for optimizing the performance and user experience of your Next.js applications.