Go-To Guide to Server-Side Rendering in React
- Pravaah Consulting
- 1 day ago
- 11 min read
Server-side rendering (SSR) with React is rapidly becoming a cornerstone for creating modern, highly performant web experiences. By generating the initial HTML on the server before sending it to the user's browser, SSR enables faster load times, seamless SEO optimization, and accessibility even when client-side JavaScript is limited or disabled. This technique is ideal for dynamic applications that require instant content visibility, ensuring users and search engines can access fully rendered pages immediately.
In this guide, you'll discover how SSR works in React, why it offers significant advantages over client-only rendering, and practical strategies for integrating SSR into your projects. You'll also learn about typical challenges, such as maintaining application state across the server and client, and how to tackle common pitfalls to maximize both performance and maintainability.
What is Server-Side Rendering in React?

Put simply, server-side rendering in React is the technique of rendering your app’s components to HTML on the server before sending them to the browser. This means users get a pre-built page instantly, rather than waiting for JavaScript to run before seeing any content.
Compared to client-side rendering (CSR), where the browser handles all rendering, React renders server-side, improving speed, SEO, and accessibility for a wider audience.
Most modern React projects use server-side rendering with ReactJS, powered by frameworks like Next.js or Express.js, allowing applications to deliver dynamic content without sacrificing performance.
Why Should You Use Server-Side Rendering with React?
Faster initial page load: SSR sends fully rendered HTML from the server, so users see the content right away, no loading spinners, no blank screens.
SEO boost: Search engines crawl HTML much more easily than JavaScript, so your pages get indexed and ranked higher.
Better social sharing: Links shared on social platforms display correct previews and metadata, thanks to server-side React rendering.
Consistency across devices: SSR ensures even users with older devices or slow networks get a functional, fast experience.
Is server-side rendering React.js worth it? For marketplaces, e-commerce, healthcare portals, and tech service websites, SSR delivers optimal page speed and discoverability.
How Does Server-Side Rendering Work in ReactJS?
When a user requests a page, the server generates the necessary HTML using React components, fetches any required data, and sends this complete markup to the browser. JavaScript on the client “hydrates” the page, attaching event listeners and enabling full interactivity going forward (just like a regular React SPA).
Frameworks like Next.js simplify building SSR apps—just export a getServerSideProps method to fetch data and render on the server.
With Express.js, you can configure middleware to render with React and return HTML, though it requires more custom work.
Server-side rendering with React isn’t just for advanced use cases; it's for everyday projects where performance, SEO, or accessibility matter.
Implementing Server-Side Rendering in React
Let’s now dive a bit deeper into how server-side rendering works in Next.js and Express.js by exploring a use case where it can be particularly useful.
Use Case: An E-Commerce Website
Let’s consider a use case where server-side rendering can be particularly useful.
An e-commerce website typically has many pages. Each page displays a product or product category. Additionally, these pages are typically dynamic and frequently updated, so it’s essential to ensure they are easily discoverable by search engines and accessible to all users.
To achieve this, you can build your e-commerce website with server-side rendering in Next.js or Express.js. This approach would allow you to generate an HTML markup for each page on the server. This makes it easy for search engines to crawl and index the content.
Implementing Server-Side Rendering using Next.js
Let’s now look into how we can implement server-side rendering in Next.js for an e-commerce website.
Step 1: Create a new Next.js project
To get started, you will have to create a new Next.js project by running the following commands in your terminal:
npx create-next-app my-ecommerce-app
cd my-ecommerce-appStep 2: Add required dependencies
Next, you’ll need to add the following dependencies to your project:
npm install react react-dom nextThis is how the package.json looks right now.
{
"name": "my-ecommerce-app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@next/font": "13.1.6",
"eslint": "8.34.0",
"eslint-config-next": "13.1.6",
"next": "13.1.6",
"react": "18.2.0",
"react-dom": "18.2.0"
}
}Please note the versions of the packages mentioned above, as they were used when this guide was created.
Step 3: Set up the environment configuration
We will now define an environment variable to be stored in a .env.local file, which stores configuration settings that can be used across different environments (e.g. development, staging, production).
To set an environment variable in your project. You will have to create a file called .env.local in the root of your project directory and add a line like the following:
API_URL=http://localhost:3000It is important to note that you should not check this file into source control, as it may contain sensitive information such as database credentials or API keys.
Instead, you can create a template file called .env.example that contains placeholder values for your environment variables and check that file into source control. Other developers can then copy this file and fill in the values for the environment variables.
Step 4: Create a new page
Next.js uses a file-based routing system, which means that a file in the pages directory represents each page in your application. To create a new page, simply create a new file in the page’s directory with the desired URL path. For example, to create a page displaying a product list, you could create a file called pages/products/index.js.
In this file, you can define a React component that will be rendered when the user visits the /products URL path. Here’s an example component that fetches a list of products from an API and displays them in a list:
function ProductsPage() {
const [products, setProducts] = useState([])
useEffect(() => {
async function fetchProducts() {
const res = await fetch('/api/products')
const products = await res.json()
setProducts(products)
}
fetchProducts()
}, [])
return (
<div>
<h1>Products</h1>
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
</div>
)
}
export default ProductsPage
Step 5: Create an API endpoint
To fetch the list of products, we’ve used an API endpoint at /api/products. Next.js provides a built-in API routing system that makes it easy to create serverless API endpoints.
To create an API endpoint, create a new file in the pages/api directory. For example, to create an API endpoint that returns a list of products, you could create a file called pages/api/products.js.
In this file, you can define a function that will be executed when the user requests the API endpoint. For the purpose of this guide, we will use an example function that fetches a list of products from a mock API:
const products = [ { id: 1, name: 'Product 1' }, { id: 2, name: 'Product 2' }, { id: 3, name: 'Product 3' },]
export default function handler(req, res) {
res.status(200).json(products)
}Step 6: Update the page to use server-side rendering
By default, Next.js uses client-side rendering (CSR) to render pages, which means that the JavaScript code is executed in the user’s browser. To switch to server-side rendering (SSR), you’ll need to update your page component to use a getServerSideProps function.
The getServerSideProps function is a special function that runs on the server before the page is rendered. It can be used to fetch data from an API or database, and return it as props to the page component.
Here’s an updated version of the pages/products/index.js file that uses getServerSideProps to fetch the list of products on the server:
import { useState } from 'react'
function ProductsPage({ products }) {
const [loading, setLoading] = useState(false)
return (
<div>
<h1>Products</h1>
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
</div>
)
}
export async function getServerSideProps() {
const res = await fetch(`${process.env.API_URL}/api/products`)
const products = await res.json()
return { props: { products } }
}
export default ProductsPageNote that we’ve moved the useState hook for the loading state outside of the getServerSideProps function, since it needs to be initialized on the client as well.
Step 7: Start the development server
You can now start the development server by running the following command in your terminal:
npm run devThis will start a local development server at http://localhost:3000.
Step 8: Test the application
You can now test the application by visiting the /products URL path in your web browser. You should see a list of products displayed on the page.
If you view the page source in your browser, you’ll also see that the list of products is included in the HTML markup, meaning the page was rendered on the server.
Congratulations, you now know how to implement server-side rendering in a Next.js application!
Implementing Server-Side Rendering using Express.js
Let’s now look into how we can implement the same use case in an Express.js application:
Step 1: Create a new Express.js application
To get started, you will have to create a new directory for your project and run the following command in your terminal:
npm initYou can now see a package.json file in your project directory.
Next, install Express.js and the necessary dependencies by running the following command:
npm install express react react-dom nextThis is how the package.json looks right now.
{
"name": "express-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.2",
"next": "^13.1.6",
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}Please note the versions of the packages mentioned above, as they were used when this guide was created.
Step 2: Set up the environment configuration
We will now define an environment variable to be stored in a .env.local file, which stores configuration settings that can be used across different environments (e.g. development, staging, production).
To set an environment variable in your project. You will have to create a file called .env.local in the root of your project directory and add a line like the following:
API_URL=http://localhost:3000It is important to note that you should not check this file into source control, as it may contain sensitive information such as database credentials or API keys.
Instead, you can create a template file called .env.example that contains placeholder values for your environment variables and check that file into source control. Other developers can then copy this file and fill in the values for the environment variables.
Step 3: Set up the server
Now create a new file called server.js in the root of your project directory, and add the following code:
const express = require('express')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
const server = express()
server.get('/', (req, res) => {
return app.render(req, res, '/home')
})
server.get('/products', (req, res) => {
return app.render(req, res, '/products')
})
server.all('*', (req, res) => {
return handle(req, res)
})
server.listen(3000, (err) => {
if (err) throw err
console.log('> Ready on http://localhost:3000')
})
})This code sets up an Express.js server that listens for incoming requests on port 3000. The app object is a Next.js application instance, which we use to render pages in response to requests.
In this example, we’ve set up two routes: / and /products. When a request comes in for either of these routes, the server calls app.render() to render the corresponding page. If the requested route doesn’t match either of these, the server falls back to the handle function, which serves the appropriate page using client-side rendering.
Step 4: Create the home page
Create a new file called pages/home.js in a directory called pages in the root of your project directory, and add the following code:
import Link from 'next/link'
function HomePage() {
return (
<div>
<h1>Welcome to our e-commerce website!</h1>
<Link href="/products">
<a>View our products</a>
</Link>
</div>
)
}
export default HomePageThis code defines a simple home page that displays a welcome message and a link to view the products page.
Step 5: Create the products page
Create a new file called pages/products.js in the pages directory, and add the following code:
import { useEffect, useState } from 'react';
function ProductsPage() {
const [products, setProducts] = useState([]);
useEffect(() => {
async function fetchProducts() {
const response = await fetch('/api/products');
const data = await response.json();
setProducts(data.products);
}
fetchProducts();
}, []);
return (
<div>
<h1>Products</h1>
<ul>
{products.map(product => (
<li key={product.id}>{product.name}</li>
))}
</ul>
</div>
);
}
export default ProductsPage;This code defines a products page that displays a list of products fetched from an API endpoint. The useEffect hook is used to manage state and fetch data from the server. When the component mounts, the useEffect hook calls the fetchProducts function to retrieve the products from the API.
Step 6: Create the API endpoint
You will now have to add an API endpoint for products in server.js file present in the root of your project directory, and add the following code:
server.get('/api/products', (req, res) => {
const products = [
{ id: 1, name: 'Product 1' },
{ id: 2, name: 'Product 2' },
{ id: 3, name: 'Product 3' },
];
res.status(200).json({ products });
});This code defines an API endpoint that returns a list of products. This endpoint would fetch data from a database or other data source in a real-world scenario.
The updated server.js should look as follows:
const express = require('express')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
const server = express()
server.get('/', (req, res) => {
return app.render(req, res, '/home')
})
server.get('/products', (req, res) => {
return app.render(req, res, '/products')
})
server.get('/api/products', (req, res) => {
const products = [
{ id: 1, name: 'Product 1' },
{ id: 2, name: 'Product 2' },
{ id: 3, name: 'Product 3' },
];
res.status(200).json({ products });
});
server.all('*', (req, res) => {
return handle(req, res)
})
server.listen(3000, (err) => {
if (err) throw err
console.log('> Ready on http://localhost:3000')
})
})Step 7: Start the server
Start the Express.js server by running the following command in your terminal:
node server.js
This will start the server and make it available at http://localhost:3000.
When you navigate to http://localhost:3000/, you should see the home page with a link to the products page.
Clicking the link should take you to the products page, displaying a list of products fetched from the API endpoint.
Congratulations, you now know how to implement server-side rendering in a Next.js and an Express.js application!
Advantages and Disadvantages of server-side React Rendering
The benefits of React.js's server-side rendering especially shine for dynamic apps with frequent content updates, product catalogs, and resource-heavy landing pages.
When Should You Use Server-Side Rendering in React JS?
Consider SSR for apps where:
SEO and fast initial loads are critical (e.g., e-commerce and B2B service sites).
Content must appear instantly for users on varying devices.
You need dynamic social meta tags or Open Graph previews.
Complex data fetching and backend logic dictate what’s displayed.
For single-page apps where SEO isn’t as important (such as internal dashboards), client-side solutions still work well.
Best Practices for SSR with React
Use frameworks like Next.js for easier SSR setup, routing, and data fetching.
Keep API endpoints optimized for fast data delivery.
Implement caching to prevent unnecessary server render cycles.
Ensure critical CSS and scripts are loaded efficiently for best performance.
Use A/B testing to compare user experience and conversion metrics with/without SSR.
Monitor server health and performance—SSR can increase server load, so scale accordingly.
Let Pravaah Consulting’s expertise in agentic AI and custom solutions help your business leverage server-side rendering with React.js to deliver top-tier digital products.
FAQs
1. What is server-side rendering in React, and how does it improve SEO?
Server-side rendering in React generates full HTML for each page on the server, making your site easily indexable by search engines and boosting your SEO rankings.
2. How does React server-side benefit user experience?
Users see your content instantly when the HTML is rendered on the server, instead of waiting for client-side JavaScript, leading to faster page loads and better engagement.
3. When is server-side rendering with ReactJS the right choice for a project?
SSR suits apps where SEO, performance, and dynamic content delivery are essential, such as marketplaces, healthcare portals, and complex B2B platforms.
4. Can server-side rendering with React be combined with client-side navigation?
Yes, once the initial render completes, React hydrates the page and enables smooth SPA-style navigation, so you get the best of both worlds.
5. What are the challenges of React and server-side rendering?
SSR can involve more infrastructure, more advanced data fetching, and higher server resource usage than traditional client-side React apps.
6. Is React.js server-side rendering suitable for small business websites?
Absolutely! Even small business sites can benefit from SSR, especially if they want better Google rankings and instant load times for customers.
7. How do I get started with server-side React rendering for my business app?
Start by choosing a framework like Next.js, set up your data fetching logic, and test performance in your staging environment. Pravaah Consulting can help architect, build, and measure SSR solutions to match your unique needs.
