SSR – Server-Side Rendering is one of the most powerful features of Next.js, a popular React framework. It allows developers to render pages on the server before sending them to the browser, improving performance, SEO, and user experience.
What is Server-Side Rendering (SSR)?
Server-Side Rendering refers to the process of rendering web pages on the server instead of the client-side browser. The server generates the complete HTML for a page and sends it to the browser. This differs from the default Client-Side Rendering (CSR) used in React, where the browser is responsible for rendering the page using JavaScript.
How SSR Works in Next.js
Next.js facilitates SSR seamlessly. When a request is made to a page with SSR enabled:
- The server processes the request and executes the necessary logic.
- Data fetching functions (like
getServerSideProps
) run to retrieve data. - The page is rendered with the fetched data into static HTML.
- The HTML is sent to the browser, which hydrates the page to make it interactive.
Why Use Server-Side Rendering (SSR)?
Key Benefits of SSR : Server-Side Rendering
- Improved SEO: Search engine crawlers can index pre-rendered HTML more effectively.
- Faster First Paint: Since the server sends fully-rendered HTML, the initial load is faster.
- Dynamic Content: SSR is ideal for pages where the content changes frequently or depends on the user’s request.
- Better Performance on Slow Devices: Offloading rendering to the server reduces the workload on the client’s device.
Use Cases for SSR: Server-Side Rendering
- Blogs and articles requiring SEO optimization.
- E-commerce platforms with frequently updated product details.
- Personalized dashboards or pages based on user authentication.
- News websites that need fresh content on every load.
Enabling SSR in Next.js
SSR in Next.js is implemented using the getServerSideProps
function. This function fetches data and pre-renders the page for every request.
Basic Example of getServerSideProps
import React from 'react';
export async function getServerSideProps(context) {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return {
props: { data },
};
}
export default function Page({ data }) {
return (
<div>
<h1>Data from Server:</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
Explanation:
getServerSideProps
is called at request time.- It fetches data from an external API and passes it to the component as props.
- The server renders the page with the data before sending it to the browser
Lifecycle of SSR in Next.js
The SSR lifecycle in Next.js encompasses all the steps involved in generating, rendering, and delivering a web page when SSR is enabled. Unlike Client-Side Rendering (CSR), where the browser handles rendering, SSR performs these tasks on the server. The lifecycle can be divided into the following phases:
- Request Phase: The server receives a request for a specific page.
- Data Fetching Phase: Data required for rendering the page is fetched.
- Rendering Phase: React components are rendered to HTML on the server.
- Response Phase: The server sends the rendered HTML to the client.
- Hydration Phase: The client-side React app takes over to make the page interactive.
A. Request Phase
The SSR lifecycle begins when the server receives a request for a page. This could be a GET request from a browser or any client requesting a specific route. Next.js processes the request and determines whether the page needs to be server-rendered.
Key Players:
- HTTP request object (
req
): Contains details like headers, cookies, and query parameters. - HTTP response object (
res
): Used to send the response back to the client.
- Example:
export async function getServerSideProps(context) {
const { req, res } = context;
// Example: Redirecting unauthenticated users
if (!req.cookies.authToken) {
res.writeHead(302, { Location: '/login' });
res.end();
return { props: {} };
}
return { props: { user: req.cookies.authToken } };
}
B. Data Fetching Phase
Once the request is validated, the server fetches any data required for rendering the page. This step happens in the getServerSideProps
function, a built-in Next.js method specifically designed for SSR.
What Happens:
- Data is fetched from APIs, databases, or external sources.
- The fetched data is passed as props to the React component.
Characteristics:
getServerSideProps
runs only on the server.- It executes for every request, ensuring fresh data is always rendered.
Example:
export async function getServerSideProps(context) {
const userId = context.query.id;
const response = await fetch(`https://api.example.com/user/${userId}`);
const userData = await response.json();
return { props: { user: userData } };
}
C. Rendering Phase
After fetching the data, Next.js renders the React component tree to static HTML on the server. This process converts JSX into an HTML string that can be sent to the browser.
Server Rendering Process:
- React components are instantiated with the fetched props.
- The component tree is rendered to an HTML string.
- The HTML string is injected into the Next.js page template.
Optimizations:
- Use lightweight components for faster rendering.
- Minimize dependencies and avoid unnecessary computations during rendering.
D. Response Phase
The server sends the fully-rendered HTML, along with any necessary JavaScript, back to the client. The browser displays the pre-rendered HTML immediately, resulting in a faster first contentful paint (FCP) compared to CSR.
Server Response Components:
- HTML: Fully-rendered content.
- JavaScript Bundle: Code required to hydrate and make the page interactive.
Example:
<html>
<head>
<title>Next.js SSR Blog</title>
</head>
<body>
<div id="__next">
<h1>Welcome to my blog!</h1>
<p>This page was server-rendered.</p>
</div>
<script src="/_next/static/chunks/main.js"></script>
</body>
</html>
E. Hydration Phase
After the server delivers the pre-rendered HTML, React takes over on the client side. This process, called hydration, attaches React’s event listeners and re-establishes the component tree, making the page interactive.
Key Steps in Hydration:
- React reconciles the HTML received from the server with the component state.
- Event listeners (e.g.,
onClick
,onChange
) are added to the DOM elements.
- Example:
function MyPage({ data }) {
const handleClick = () => alert('Button clicked!');
return (
<div>
<h1>Server-Rendered Page</h1>
<button onClick={handleClick}>Click Me</button>
</div>
);
}
Advanced Features of SSR in Next.js
1. Data Fetching in SSR
Using Context in getServerSideProps
: Access request headers, cookies, or query parameters.
export async function getServerSideProps(context) {
const { query } = context;
const response = await fetch(`https://api.example.com/data?id=${query.id}`);
const data = await response.json();
return { props: { data } };
}
2. Error Handling
export async function getServerSideProps() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) throw new Error('Failed to fetch data');
const data = await response.json();
return { props: { data } };
} catch (error) {
return { props: { error: error.message } };
}
}
3. Using Middleware with SSR
Middleware can be combined with SSR for tasks like authentication or redirection.
export async function getServerSideProps(context) {
const { req, res } = context;
if (!req.cookies.token) {
res.writeHead(302, { Location: '/login' });
res.end();
return { props: {} };
}
return { props: { user: req.cookies.token } };
}
Performance Optimization in SSR
Tips for Optimizing SSR
- Minimize Data Fetching:
- Fetch only the data needed for rendering the page.
- Use optimized APIs or caching mechanisms.
- Leverage CDN:
- Serve static assets and cache rendered pages where possible.
- Avoid Blocking Operations:
- Keep computations lightweight in
getServerSideProps
.
- Keep computations lightweight in
- Prefetch Data:
- Use
Link
components with prefetching enabled for seamless navigation
- Use
Conclusion
Server-Side Rendering in Next.js is a robust feature that caters to modern web application needs. By understanding its principles, implementation, and best practices, developers can build highly efficient, SEO-friendly, and dynamic web applications.
Check out more blogs