Subscribe
How to Implement Server-Side Rendering with Express and React
6 mins read

By: vishwesh

How to Implement Server-Side Rendering with Express and React

If you're building a web application using React, you might have heard about server-side rendering (SSR). This technique involves rendering React components on the server and sending HTML to the client instead of sending JavaScript that would be executed on the client-side. SSR has several benefits, including faster load times, better SEO, and improved user experience.

In this tutorial, we'll learn how to implement server-side rendering with Express and React. We'll create a simple React application and use Express to set up a server that renders the React components on the server. We'll cover the following topics:

  1. Setting up a new React project
  2. Creating a simple React component
  3. Setting up Express server
  4. Implementing server-side rendering
  5. Rendering data from an API
  6. Handling errors
  7. Deploying the application

1. Setting up a new React project

The first step is to set up a new React project. You can use Create React App to quickly set up a new project with all the necessary dependencies:

 code

npx create-react-app my-app
cd my-app
npm start

This will create a new React project and start a development server at http://localhost:3000.

2. Creating a simple React component

Next, we'll create a simple React component that we can render on the server. We'll create a new file src/App.js with the following content:

import React from 'react';

function App() {
  return (
    <div>
      <h1>Hello, world!</h1>
      <p>This is a simple React component.</p>
    </div>
  );
}

export default App;

This component just renders a heading and a paragraph of text.

3. Setting up Express server

To render the React component on the server, we need to set up an Express server. Create a new file server.js with the following content:

const express = require('express');
const path = require('path');
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const App = require('./src/App').default;

const app = express();

app.use(express.static(path.join(__dirname, 'build')));

app.get('/', (req, res) => {
  const html = ReactDOMServer.renderToString(<App />);
  res.send(`
    <!DOCTYPE html>
    <html>
      <head>
        <title>Server-side Rendering with Express and React</title>
      </head>
      <body>
        <div id="root">${html}</div>
        <script src="/static/js/main.chunk.js"></script>
        <script src="/static/js/0.chunk.js"></script>
        <script src="/static/js/bundle.js"></script>
      </body>
    </html>
  `);
});

const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`Server is listening on port ${port}`);
});

This sets up an Express server that serves static files from the build directory and renders the React component when the root URL is requested. We use ReactDOMServer.renderToString to render the component to a string of HTML that we can send to the client.

4. Implementing server-side rendering

Now that we have set up the server, we can test the server-side rendering by running the following commands:

npm run build
node server.js

This will build the React application and start the server. If you open http://localhost:3000 in your browser, you should see the rendered HTML.

5. Rendering data from an API

Now that we have the basic server-side rendering set up, we can fetch data from an API and render it on the server. We'll use the JSONPlaceholder API as an example.

First, let's create a new component src/PostList.js that fetches data from the API and renders a list of posts:

import React, { useState, useEffect } from 'react';

function PostList() {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/posts')
      .then(response => response.json())
      .then(data => setPosts(data));
  }, []);

  return (
    <div>
      <h2>Posts</h2>
      <ul>
        {posts.map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}

export default PostList;

This component fetches the list of posts from the API and renders a list of post titles.

Next, let's update our App component to render the PostList component:

import React from 'react';
import PostList from './PostList';

function App() {
  return (
    <div>
      <h1>Hello, world!</h1>
      <p>This is a simple React component.</p>
      <PostList />
    </div>
  );
}

export default App;

Finally, we need to update our server to render the App component and pass the data from the API as props to the PostList component. We'll use the ReactDOMServer.renderToString method to render the component to a string, and then use the ReactDOMServer.renderToStaticMarkup method to render the component with the props as HTML attributes.

import React from 'react';
import ReactDOMServer from 'react-dom/server';
import App from './src/App';
import PostList from './src/PostList';

app.get('/', (req, res) => {
  fetch('https://jsonplaceholder.typicode.com/posts')
    .then(response => response.json())
    .then(posts => {
      const html = ReactDOMServer.renderToString(
        <App>
          <PostList posts={posts} />
        </App>
      );

      const data = JSON.stringify(posts);
      const script = `
        <script>
          window.__DATA__ = ${data};
        </script>
      `;

      const fullHTML = `
        <!DOCTYPE html>
        <html>
          <head>
            <title>Server-side Rendering with Express and React</title>
          </head>
          <body>
            <div id="root">${html}</div>
            <script src="/static/js/main.chunk.js"></script>
            <script src="/static/js/0.chunk.js"></script>
            <script src="/static/js/bundle.js"></script>
            ${script}
          </body>
        </html>
      `;
      
      res.send(fullHTML);
    })
    .catch(error => {
      console.error(error);
      res.status(500).send('An error occurred');
    });
});

Here, we fetch the list of posts from the API and pass it as a prop to the PostList component. We also stringify the data and include it in a script tag so that the client can access it later.

6. Handling errors

When working with APIs, it's important to handle errors that might occur during the fetch. We can update our PostList component to render an error message if the API call fails:

import React, { useState, useEffect } from 'react';

function PostList() {
  const [posts, setPosts] = useState([]);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/posts')
      .then(response => {
        if (!response.ok) {
          throw new Error('Failed to fetch posts');
        }
        return response.json();
      })
      .then(data => setPosts(data))
      .catch(error => setError(error.message));
  }, []);

  if (error) {
    return <div>{error}</div>;
  }

  return (
    <div>
      <h2>Posts</h2>
      <ul>
        {posts.map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}

export default PostList;

Here, we added a new state variable error that will hold the error message if the API call fails. In the useEffect hook, we throw an error if the response is not successful, and catch it to set the error state variable. In the render method, we check if error is not null, and render an error message instead of the post list.

We can also update our server to set the HTTP status code to 500 if an error occurs:

app.get('/', (req, res) => {
  fetch('https://jsonplaceholder.typicode.com/posts')
    .then(response => {
      if (!response.ok) {
        throw new Error('Failed to fetch posts');
      }
      return response.json();
    })
    .then(posts => {
      const html = ReactDOMServer.renderToString(
        <App>
          <PostList posts={posts} />
        </App>
      );

      const data = JSON.stringify(posts);
      const script = `
        <script>
          window.__DATA__ = ${data};
        </script>
      `;

      const fullHTML = `
        <!DOCTYPE html>
        <html>
          <head>
            <title>Server-side Rendering with Express and React</title>
          </head>
          <body>
            <div id="root">${html}</div>
            <script src="/static/js/main.chunk.js"></script>
            <script src="/static/js/0.chunk.js"></script>
            <script src="/static/js/bundle.js"></script>
            ${script}
          </body>
        </html>
      `;

      res.send(fullHTML);
    })
    .catch(error => {
      console.error(error);
      res.status(500).send('An error occurred');
    });
});

Here, we added a catch block to the promise chain to set the HTTP status code to 500 and send an error message to the client.

Conclusion

In this article, we learned how to implement server-side rendering with Express and React. We started by creating a basic server that rendered a React component to HTML, and then added support for static files and client-side hydration. We then fetched data from an API and rendered it on the server, and handled errors that might occur during the fetch. With server-side rendering, we can improve the performance and SEO of our web applications, and provide a better user experience for our users.

Recent posts

Don't miss the latest trends

    Popular Posts

    Popular Categories