Server Side Rendering (SSR), also known as Server-Side Rendering, refers to the process of rendering a JavaScript application on the server instead of the browser.
There are several reasons why we should consider using SSR with React:
- Faster first page load time: SSR allows your site to load faster, enhancing the user experience.
- Improved SEO: Search engines struggle to efficiently index applications that rely solely on client-side rendering. SSR helps search engines understand and index your content correctly.
- Better social media sharing: SSR enables easy gathering of metadata, such as images, title, and description, when sharing pages on social media.
Without SSR, the server only sends an HTML page with script tags, leaving the browser to render the application. Although client-rendered apps excel at subsequent user interactions, SSR allows us to strike a balance between client-rendered apps and backend-rendered apps. The server generates the page, while the client handles interactions after the initial load.
However, SSR comes with its drawbacks:
- Increasing complexity: As the application becomes more complex, SSR can become more challenging to implement.
- Resource-intensive: Rendering a large application server-side can consume significant resources and potentially lead to slower performance, especially under heavy load.
Let’s start with a basic example to understand how to implement SSR:
To implement basic SSR, we’ll use Express. If you’re not familiar with Express, check out my free Express Handbook here.
Here is a simplified setup to render a basic React app with SSR:
- Install Express by running
npm install express
. - Create a new folder called
server
in your app directory. - Inside the
server
folder, create a file namedserver.js
. - Import the necessary modules and components:
import path from 'path'
import fs from 'fs'
import express from 'express'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import App from '../src/App'
- Set up the server:
const PORT = 8080
const app = express()
const router = express.Router()
- Create a server rendering function:
const serverRenderer = (req, res, next) => {
fs.readFile(path.resolve('./build/index.html'), 'utf8', (err, data) => {
if (err) {
console.error(err)
return res.status(500).send('An error occurred')
}
return res.send(
data.replace(
'<div id="root"></div>',
`<div id="root">${ReactDOMServer.renderToString(<App />)}</div>`
)
)
})
}
- Set up the router:
router.use('^/$', serverRenderer)
router.use(express.static(path.resolve(__dirname, '..', 'build'), { maxAge: '30d' }))
app.use(router)
- Update the client application:
In your src/index.js
, replace ReactDOM.render()
with ReactDOM.hydrate()
:
ReactDOM.hydrate(<App />, document.getElementById('root'))
- Transpile the Node.js code using Babel:
Install the required packages:
npm install @babel/register @babel/preset-env @babel/preset-react ignore-styles
Create an entry point in server/index.js
:
require('ignore-styles')
require('@babel/register')({
ignore: [/(node_modules)/],
presets: ['@babel/preset-env', '@babel/preset-react']
})
require('./server')
- Build the React application:
npm run build
- Start the server:
node server/index.js
Please note that this is a simplistic approach. For more complex scenarios, you may need additional configurations and libraries to handle things like images and page header metadata.
When it comes to SSR, using pre-made libraries and tools designed specifically for this purpose is highly recommended. Two popular options for SSR with React are Next.js and Gatsby.