Home/Blog/Web Development/Understanding rendering in Next.js
Home/Blog/Web Development/Understanding rendering in Next.js

Understanding rendering in Next.js

Farrukh Rasool
Oct 24, 2024
7 min read

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.

Next.js
Next.js

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

Cover
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, and how to deploy your application. 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.

5hrs
Intermediate
12 Playgrounds
6 Quizzes

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 fetching
return <div>{data}</div>; // Rendered on the server
}
Server component
  • 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 management
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
Client component

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>;
}
Static rendering

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>;
}
Dynamic rendering

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.

Streaming
Streaming

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>
);
}
Streaming

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 localStorage
useEffect(() => {
const storedLikes = localStorage.getItem(`postLikes-${postId}`);
if (storedLikes) {
setPostLikes(JSON.parse(storedLikes));
}
}, [postId]);
// Function to handle like button click
const handleLike = () => {
const newLikes = postLikes + 1;
setPostLikes(newLikes);
// Save updated like count to localStorage
localStorage.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>
);
}
Client Component

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 hydratedHydration 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., making the UI interactive.

  • 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., onClick, onChange)

No

Yes

Use state and life cycle hooks (e.g., useState, useEffect)

No

Yes

Utilize browser-specific APIs (e.g., localStorage, geolocation)

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>
);
}
Passing a Server Component as a prop to a Client Component

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?

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.

What is the difference between static rendering and dynamic rendering?

How does streaming improve page load times?

What is the "use client" directive, and how does it work?

Can I use both Server and Client Components together in Next.js?


  

Free Resources