Integrate SSR on react app which is installed with create-react-app

By : FiveMinute  |  Updated On : 3 days ago

Integrate SSR on react app which is installed with create-react-app

In today's programming JavaScript is widely used in all the platforms like Web, Mobile App, IOT and so many other things. Single page application with JavaScript is trending in the technology world like angular, react, vue, etc. To create a user interface for the web with react it's very easy to do it, but the question comes to make the app search engine optimization with all meta tags and source codes download on the client browser for this you have to integrate nodejs with your react application which executes the same js on your server and download the requested url on client browser so that Google or other search engines can easily crawl or read each and everything. Also, SSR makes your application fast.

To make your app SEO Friendly, you have to integrate your app with SSR (Server side rendering) which we talk about here step by step integration which is very easy to understand all the things.

Install yours react app with the below command.

$ npx create-react-app my-app --template redux

OR

$ npx create-react-app my-app

Note : If below error comes with the above command than install globally create-react-app first.

$ npx create-react-app my-app --template redux
Error: EPERM: operation not permitted, mkdir 'C:\Users\Amit'
command not found: create-react-app

To solve above issue you need to install globally create-react-app with below command.

$ npm install -g create-react-app

After globally install create-react-app, now install your app with above mentioned command.

Then, move to the project directory

$ cd my-app

First, start your client side application with the below command to verify the installation.

$ npm run start

If the above command opens react app in your browser it means react JS installed successfully.

You can see in the view-source of your code "<div id="root"></div>" is empty as given in the below example.

reactjs view source code

Now start with the SSR step by step with your react app.

In src/index.js use ReactDOM.hydrate instead of ReactDOM.render to indicate to the DOM renderer that we’re rehydrating the app after a server-side render.

ReactDOM.hydrate(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

Now create an express server and render the reactJS application. For this, you have to install the below packages.

$ npm install express@4.17.1
 
Or, using yarn:

$ yarn add express@4.17.1

Next, create a server directory next to the app’s src directory:

$ mkdir server
Than move to server directory and create index.js file.
$ cd server

//create index.js via touch command
$ touch index.js

    put the below code on server/index.js

    import path from 'path';
    import fs from 'fs';
    
    import React from 'react';
    import express from 'express';
    import ReactDOMServer from 'react-dom/server';
    
    import App from '../src/App';
    
    const PORT = process.env.PORT || 3006;
    const app = express();

    Next, add the server code with some error handling in server/index.js :

    // ...
    
    app.get('/', (req, res) => {
      const app = ReactDOMServer.renderToString(<App />);
    
      const indexFile = path.resolve('./build/index.html');
      fs.readFile(indexFile, 'utf8', (err, data) => {
        if (err) {
          console.error('Something went wrong:', err);
          return res.status(500).send('Oops, better luck next time!');
        }
    
        return res.send(
          data.replace('<div id="root"></div>', `<div id="root">${app}</div>`)
        );
      });
    });
    
    app.use(express.static('./build'));
    
    app.listen(PORT, () => {
      console.log(`Server is listening on port ${PORT}`);
    });

    Below are the 3 important points we are doing here.

    1. We tell the express server to read the content of build/index.html.
    2. We use a method from ReactDOMServerrenderToString, to render our app to a static HTML string.
    3. We then read the static index.html file from the built client app, inject our app’s static content in the <div> with an id of "root", and send that as the response to the request.

    Now configure webpack, Babel, and npm Scripts for SSR.

    Code to work on the server with nodejs it needs to transpile the code using webpack and babel. To accomplish this need to install the below packages using below command.

    Now come out from the server directory to install the packages.

    $ cd ..

    Here we are not installing webpack package as it is already installed by the react. If we install it then version confliction will be shown. Now execute the below command to install all required packages in the dev dependencies.

    $ npm install webpack-cli@3.3.12 webpack-node-externals@1.7.2 @babel/core@7.10.4 babel-loader@8.1.0 @babel/preset-env@7.10.4 @babel/preset-react@7.10.4 --save-dev

    Note: An earlier version of this tutorial installed babel-corebabel-preset-env, and babel-preset-react-app. These packages have since been archived, and the mono repo versions are utilized instead.

    Install 2 more packages in dev dependencies to transpile CSS and SVG as it is already using in react application.

    $ npm install css-loader url-loader -save-dev

    Next, create a Babel configuration file in your project’s root directory:

    $ touch .babelrc.json

    Now open the .babelrc.json in your editor and paste the below code.

    {
      "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
      ]
    }

    Note: An earlier version of this tutorial used a .babelrc file (no .json file extension). This was a configuration file for Babel 6, but this is no longer the case for Babel 7.

    Now, we will create a webpack config for the server that uses Babel Loader to transpile the code. Start by creating the webpack.server.js file in your project root directory:

    $ touch webpack.server.js

      Next, paste the below code in webpack.server.js

      const path = require('path');
      const nodeExternals = require('webpack-node-externals');
      
      module.exports = {
          entry: './server/index.js',
          target: 'node',
          externals: [nodeExternals()],
          output: {
              path: path.resolve(__dirname, './build'),
              filename: 'server.js'
          },
          module: {
              rules: [
                  {
                      test: /\.js$/,
                      use: 'babel-loader'
                  },
                  {
                      test: /\.css$/i,
                      use: ["css-loader"],
                  },
                  {
                      test: /\.(png|jpe?g|gif|svg)$/i,
                      use: [
                          {
                              loader: 'url-loader',
                              options: {
                                  limit: 8192,
                              },
                          },
                      ],
                  },
              ]
          }
      };

      With this configuration, our transpiled server bundle will be output to the build the folder in a file called server.js.

      Note the use of target: 'node' and externals: [nodeExternals()] from webpack-node-externals, which will omit the files from node_modules in the bundle; the server can access these files directly.

      Next, open the package.json and paste below scripts to run the SSR application.

      "scripts": {
          "dev:build-client": "react-scripts build",
          "dev:build-server": "webpack --config webpack.server.js --mode=development --progress --profile --colors",
          "dev:start": "node ./build/server.js",
          "dev": "npm-run-all dev:*",
          "start": "react-scripts start",
          "build": "react-scripts build",
          "test": "react-scripts test",
          "eject": "react-scripts eject"
        },

      Below points that explain how to bundle client and server scripts and also start the server.

      1. We install npm-run-all to all dev: command 1 by 1.
      2. dev:build-client : This command is used to bundle the client using react bundling strategy.
      3. dev:build-server : This command is used to bundle the server webpack configuration.
      4. dev:start : This command starts the server by using server bundle js.

       

      Now execute the below command to start the server.

      $ npm run dev

      The above command will start your server with port 3006. And you can access the application with the below URL.

      http://localhost:3006/

      Note : If you are getting the below error : 

      ReferenceError: React is not defined
          at App (webpack:///./src/App.js?:8:3)

       

      Solution.

      Than import react in App.js and every where you are using JSX code.

      import React from 'react';
      

      JSX is transformed into React.createElement() calls, thus React is required in scope. So yes, you are using React in app.js. Get used to import it whenever you use JSX or direct React.* calls.

      Now you can check in your browser view-source. "<div id="root"></div>" is not empty now.

      react view source ssr

      smiley Enjoy Coding!!!