We’ve learned that Next.js combines server-side and client-side rendering to leverage the advantages of both methods. However, we still need to determine which sections of the application are processed on the server. Is it possible to select the components to be rendered in each environment? If that’s the case, how can we achieve this?
Definitions
To address these questions, lets first revisit the definitions of ‘client’ and ‘server’. What do these terms signify?
- Client: The term ‘client’ refers to the device that you are using right now, like your smartphone or laptop. This device sends requests to a server and shows the user interface that we interact with.
- Server: A server is basically a computer, but it is configured with powerful hardware and operates without interruption. It hosts all the application’s code. When a client device sends a request, the server processes this request, performs the necessary calculations, and sends back the desired result.
In versions of Next.js prior to 13, there was a constraint in which server-side rendering was confined to single pages. This restriction implied that only route pages such as “/”, “/home”, “/about”, etc., could undergo server-side rendering. This restriction resulted in issues like prop drilling and repeated API calls when transmitting data to subcomponents. Please refer this topic: https://vercel.com/blog/nextjs-app-router-data-fetching
That is the significance of the app directory. It launched numerous features and led to a groundbreaking innovation—component-level server-side rendering. This implies that we now possess the flexibility to determine the specific locations where we want to render certain components or segments of code.
Take, for example, a component named Header.jsx. Thanks to this newfound flexibility, we have the option to choose whether it should be rendered on the server side or on the client side (within the user’s browser).
This leads us to the classification of components into two types: Client Components and Server Components.
What are these?
Both types are React components, yet they differ in their rendering locations.
- Client Component: This is a React component that runs/renders on the user’s device, like in a web browser or a mobile application.
- Server Component: This React component that runs/renders on the server, which is the infrastructure or location where the application is deployed.
Why should we opt for server-side rendering even for small components?
Consider this: when we choose to render specific components on the server, we offload some of the work from the user’s browser, which would otherwise use JavaScript to render those components. This way, the browser receives the initial HTML directly and can display the components right away. As a result, this approach decreases the size of the JavaScript bundle needed, leading to quicker initial page loads.
As mentioned earlier, we will address our challenges with the page directory. Instead of fetching data and then separately passing it to components, we will fetch the data directly within the component by making it a server component.
Choosing server-side rendering offers several advantages:
- Reduced JavaScript bundle size: The amount of JavaScript that the browser needs to download is smaller.
- Improved SEO (Search Engine Optimization): This approach enhances the visibility and indexing of your website’s content by search engines.
- Quicker initial page loading for enhanced accessibility and user experience: Users can view the content faster, which enhances the overall browsing experience.
- More effective use of server resources: Fetching data closer to the server cuts down on data retrieval times, boosting performance.
Deciding where and when to render components
The role of each component guides where it should be rendered, as implied by their name.
For components that demand user engagement—like clicking buttons, typing into input fields, initiating events, or utilizing react hooks—rendering should occur on the client side. This is because such interactions are dependent on the functionalities provided by the user’s web browser.
Conversely, components that do not necessitate user input, and are involved in activities such as data retrieval from servers, presenting static information, or executing computations on the server side, are best rendered as server components. This approach leverages the server’s capabilities, avoiding the need for specific browser functionalities.
Refer: https://nextjs.org/docs/getting-started/react-essentials#when-to-use-server-and-client-components
Eager to learn more?
A revolutionary feature of Next.js 13’s app directory is the default treatment of all components as server components. This means that, unless explicitly stated otherwise, each component is processed as a server component by default.
How do we distinguish it from the client components?
It’s quite easy: just add “use client” at the beginning of the component file. This is a clear and efficient method to signify that the component is meant to be a client component:
"use client";
const ClientComponent = () => {
console.log("Client Component");
return (
<div>
<p>This a client component</p>
</div>
);
};
export default ClientComponent;
And the Server component as
const ServerComponent = () => {
console.log("Server Component");
return (
<div>
<p>This is a server component</p>
</div>
);
};
export default ServerComponent;
Let’s experiment further with these components. Now, bring in the ClientComponent and ServerComponent inside the app/page.js file.
import ClientComponent from '@/components/ClientComponent'
import ServerComponent from '@/components/ServerComponent'
export default function Home() {
return (
<>
<ClientComponent />
<ServerComponent />
</>
)
}
Now, when we open the website in a browser, it displays the text from both the client and server components. However, only the ” Client Component ” message appears in the browser’s console log.
Whereas the terminal will show all the two console logs: “Client Component” and “Server Component”
Conclusion
In Next.js, there’s a specific design pattern to understand. When you use “use client” in a file, this command acts like a boundary. As a result, all the modules imported into this file, including any child server components, are treated as part of the client module.
Think of “use client” as a clear separation between server-side and client-side code. By establishing this boundary, you define everything within it as client-side code. A key point to remember is to avoid including server components within the client components.
Got it? If not, don’t worry. That’s where zen8labs can help!
Nam Do, Head of Learning and Development