Server Components run on the server and are ideal for tasks like fetching data from a database or API before rendering. Client Components run in the browser and are used for adding interactivity, such as handling state or user inputs. They can be combined to create hybrid applications, delivering both performance and rich user experiences.
Understanding rendering in Next.js
Key Takeaways:
Understand the different rendering strategies in Next.js, including Server and Client Components.
Learn about static, dynamic, and streaming rendering and their respective use cases.
Understand how to use Client Components for creating interactive and responsive user interfaces that utilize state, effects, and browser-specific APIs.
Understand how to combine server-side and client-side rendering to optimize performance and interactivity.
Discover when and why to use each rendering strategy for your application.
Next.js is a React framework that offers developers the ability to create high-performance, scalable, and SEO-friendly web applications with ease. There are multiple advanced concepts that make Next.js stand out from other web frameworks, e.g., rendering. In Next.js, rendering plays a vital role, as it provides a range of strategies to optimize performance, user experience, and SEO. Next.js offers different flexible rendering techniques—Server Components, Client Components, static rendering, dynamic rendering, and streaming—that help developers build fast, scalable, and user-friendly applications. In this blog, we will understand these rendering strategies and explore when and how to use them effectively.
Suppose that you’re building an online learning platform like Educative. You have all the content, e.g., interactive courses, blogs, coding challenges, and projects, with detailed explanations. However, without a well-organized system to deliver this content to the users, they may find it difficult to locate relevant content or experience slow loading times that disrupt their learning. In web development, this is known as rendering—how you present the core materials of your application (the code) to users in a clear and efficient manner.
Next.js - The ultimate way to build React apps
React is an amazing framework that allows you to build front-ends that are unmatched in speed, functionality, and ease of use. Where React falls short though is its ability to optimize for search engines. That’s where Next.js comes in. In this course, you will learn to build a giphy search app using the giphy.com API. To kick things off, you’ll learn how to statically optimize a Next.js page, creating an ultra fast loading experience for users. You’ll then dive into the inner workings of creating your giphy search app. In the back half of the course, you will learn how to optimize for SEO. By the end, you will have a great new framework to add to your resume and a new shiny application to add to your portfolio.
Rendering is the process of converting an application's code into user interfaces. With React, rendering happens mainly on the client side, dynamically updating the UI in the browser. However, Next.js allows the code to be rendered both on the client side and also on the server side. It provides us with a hybrid model for web applications where some code can be rendered on the server and some on the client. This model enables you to serve static content for courses that don't change frequently, dynamically update dashboards based on user progress, and even preload interactive elements to ensure a smooth learning experience.
Next.js supports rendering in two main environments:
On the server via Server Components: Server Components execute the application’s code on the server; the HTML is also generated on the server's end and then sent to the client. This is ideal for pages that need to fetch data from a database or an external API. Server Components also benefit from caching, allowing responses to be reused across multiple requests, improving performance, and reducing server load. Let's look at an example of defining Server Components.
export default function ServerComponent() {const data = fetchDataFromAPI(); // Server-side data fetchingreturn <div>{data}</div>; // Rendered on the server}
On the client via Client Components: Client Components are rendered in the browser, where the client executes the JavaScript code to render the UI. This approach is essential for adding interactivity to the application, handling state management, and accessing browser-specific APIs. Client Components allows for immediate feedback and dynamic updates to the UI without requiring a full page reload. To use Client Components, you must add the
"use client"directive at the top of a file above your imports. This directive will separate the Server and Client Components. Let's look at an example of defining Client Components.
'use client';import { useState } from 'react';export default function ClientComponent() {const [count, setCount] = useState(0); // Client-side state managementreturn (<div><p>You clicked {count} times</p><button onClick={() => setCount(count + 1)}>Click me</button></div>);}
Want to build something interactive? Try this project: Build an Interactive E-Library Using Next.js and Tailwind.
Key differences between Server and Client Components:
Feature | Server Components | Client Components |
Execution | Runs on the server and generates HTML | Runs in the browser, executes JavaScript |
Interactivity | Non-interactive, static | Interactive, handles user input and updates |
Performance | Faster initial load, less JavaScript on the client | Slower initial load but enables rich interactivity |
Use Case | Fetching data from databases, pre-rendering HTML | Handling forms, client-side state management |
Server Components rendering#
Next.js offers three distinct rendering strategies for Server Components, each suited to different use cases.
Static rendering#
The pages are pre-rendered at build time, producing static HTML files that can be served directly from a Content Delivery Network (CDN). This method is ideal for pages where the content does not change frequently, such as marketing pages, blogs, or documentation sites. The main pros of this strategy are its fast initial load times, reduced server load, and improved scalability.
Let's look at an example of static rendering.
export async function getStaticProps() {const data = await fetchData();return { props: { data } };}export default function StaticPage({ data }) {return <div>{data}</div>;}
Dynamic rendering#
The pages are rendered on each request, allowing for dynamic content that changes based on the user’s input or other request-specific data, such as cookies, session information, or URL parameters. The main pros of this strategy are personalized content, real-time updates, and the ability to handle user-specific data securely on the server.
Let's look at an example of dynamic rendering.
export async function getServerSideProps(context) {const data = await fetchDynamicData(context);return { props: { data } };}export default function DynamicPage({ data }) {return <div>{data}</div>;}
Key differences between static and dynamic rendering:
Feature | Static Rendering | Dynamic Rendering |
Timing | Pre-rendered at build time | Rendered on every request |
Performance | Faster initial load | Slower due to real-time generation |
SEO Impact | Excellent, as HTML is pre-rendered and crawlable | Good, but dependent on request time |
Example Use Cases | Blogs, marketing pages | Dashboards, user profiles, e-commerce |
Streaming#
It allows the server to send parts of the page to the client as they become ready rather than waiting for the entire page to be generated. This technique leverages React’s Suspense component to load UI components progressively, improving the performance by displaying content as soon as possible.
The main pros of this strategy are faster initial page load times, especially for complex or data-heavy pages, and a better user experience as content appears incrementally.
Let's look at an example of streaming.
import { Suspense } from 'react';export default function StreamingPage() {return (<div><Suspense fallback={<div>Loading...</div>}><AsyncComponent /></Suspense></div>);}
Client Components rendering#
Client Components, on the other hand, are crucial for creating interactive and dynamic user interfaces. On initial page load, Client Components are rendered statically on the server as part of the initial HTML, providing a fast, non-interactive preview. Once the client-side JavaScript is fully loaded, these components become interactive. Client Components also have access to browser APIs, like geolocation or localStorage.
To create Client Components in Next.js, you need to specify a boundary between server and client code using the "use client" directive. This directive should be placed at the top of the component file, above all imports.
Let's look at a real-life example of how to define a Client Component:
'use client';import { useState, useEffect } from 'react';export default function LikesCounter({ postId }) {const [postLikes, setPostLikes] = useState(0);// Use effect to load likes from localStorageuseEffect(() => {const storedLikes = localStorage.getItem(`postLikes-${postId}`);if (storedLikes) {setPostLikes(JSON.parse(storedLikes));}}, [postId]);// Function to handle like button clickconst handleLike = () => {const newLikes = postLikes + 1;setPostLikes(newLikes);// Save updated like count to localStoragelocalStorage.setItem(`postLikes-${postId}`, JSON.stringify(newLikes));};return (<div className="postLikes-counter"><p>This post has {postLikes} {postLikes === 1 ? 'like' : 'postLikes'}</p><button onClick={handleLike}>Like</button></div>);}
In the example above, the "use client" directive ensures that the LikesCounter component, along with its dependencies, is part of the client-side bundle.
Client Components are rendered conditionally based on the type of request:
Full page load: During an initial visit or a page reload, Next.js pre-renders a static HTML preview of both Server and Client Components on the server. This provides a fast, non-interactive view of the page. Once the client-side JavaScript is downloaded and executed, the Client Components are
, making the UI interactive.hydrated Hydration is the process of converting static HTML into a fully interactive page by attaching event listeners and initializing client-side JavaScript. This is crucial for Client Components, as it ensures that pre-rendered UI elements can respond to user interactions. Subsequent navigations: After the initial page load, Client Components are rendered entirely on the client without the server-rendered HTML.
To get hands-on experience on Next.js rendering techniques, refer to the Build a Music Sharing App with Next.js and the MERN Stack project.
Let’s look at the key use cases for Server and Client Components:
Use Case | Server Component | Client Component |
Fetch data from APIs or databases | Yes | No |
Directly access back-end resources | Yes | No |
Securely manage sensitive information (e.g., API keys, access tokens) | Yes | No |
Handle event listeners and interactivity (e.g., | No | Yes |
Use state and life cycle hooks (e.g., | No | Yes |
Utilize browser-specific APIs (e.g., | No | Yes |
Combining Server and Client Components#
One of Next.js's most useful features is the ability to combine Server and Client Components within the same application. This allows developers to perform server-side data fetching and processing while still delivering rich, interactive experiences on the client side. In the following example, the ServerComponent fetches and processes data on the server while the ClientComponent manages user interactions and updates the UI on the client side.
Let's look at an example of how we can combine Server and Client Components.
import ClientComponent from './client-component';import ServerComponent from './server-component';export default function Page() {return (<ClientComponent><ServerComponent /></ClientComponent>);}
Rendering is the backbone of how your Next.js application displays content to users. By understanding and utilizing the different rendering strategies, you can create a faster, more efficient, and user-friendly experience. Whether you’re building a static site, a dynamic dashboard, or a highly interactive application, choosing the right rendering strategy is crucial for success.
Continue learning Next.js#
Explore the following projects for hands-on practice:
Explore these courses for in-depth knowledge on Next.js routing:
Frequently Asked Questions
What is the difference between Server and Client Components in Next.js?
What is the difference between Server and Client Components in Next.js?
What is the difference between static rendering and dynamic rendering?
What is the difference between static rendering and dynamic rendering?
How does streaming improve page load times?
How does streaming improve page load times?
What is the “use client” directive, and how does it work?
What is the “use client” directive, and how does it work?
Can I use both Server and Client Components together in Next.js?
Can I use both Server and Client Components together in Next.js?