1

My two apps (a CSR and an SSR version of the same app)

I have created a standard Client Side Rendered (CSR) React application using "create-react-app". This is a client side only application whereby the whole app is deployed and run in a browser.

I have also created a Server Side Rendered (SSR or Universal) version of this application. When this application is built, it creates a server-bundle.js and a client-bundle.js. When the application runs, the browser requests the app from the server. The server returns HTML and a reference to the client-bundle.js. This client-bundle executes in the client browser to either "hydrate" the existing HTML content or render the whole content in the case of client side navigation where the server did not produce the content.

Issue with this design

I have two code bases where most of the code is identical, the styles, the components, the routes, etc. If i need to change one part of the application, I have to make sure I make the same change in both code bases.

When releasing this code to consumers, I would have to release two applications.

What I would like

I would like to have one code base that can produce a CSR application AND a SSR application.

I would like to be able to extend the configurations in my SSR app so that I could create a CSR only application like my "create-react-app". So that the application can either be built/run on one of two ways:

  1. server side app - where the server is used to get the client bundle for hydrating/rendering when client side navigation occurs OR
  2. client side app - where the a client bundle is deployed to a client browser and there is NO server running

CSR Application

Application structure

public
  index.html
src
  components
     comp1.jsx
     comp2.jsx 
     home.jsx
     page1.jsx
  styles
     styles.css
  index.jsx (contains render method containing the routes)
package.json
etc

the src/index.jsx contains

import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

import Home from './components/Home';
import Page1 from './components/Page1';

render(
  (
    <Router>
        <Switch>
          <Route exact path="/" component={Home} />
          <Route exact path="/page1" component={Page1} />
        </Switch>
    </Router>
  ),
  document.getElementById('root'),
);

To build and run the app, "react-scripts" is used. I believe "webpack-dev-server" is used to serve the application.

"build": "react-scripts build",
"start": "react-scripts start",

The react-scripts updates the index.html to add in the client bundle references and moves it into the "build" directory.

SSR/Universal Application

Application structure

public
  index.html
src
  client
     client.jsx (entry point for the client bundle, contains ReactDOM.hydrate)
  components
     comp1.jsx
     comp2.jsx 
  pages
     home.jsx
     page1.jsx
     routes.js
  server
     renderer.jsx (creates the html to return to the client)
     server.js (entry point for the server bundle, an Express server)
  styles
     styles.css
package.json
webpack.base.config.js
webpack.client.config.js (builds the client bundle)
webpack.server.config.js (builds the server bundle)
etc

The webpack.client.config.js contains

const path = require('path');
const merge = require('webpack-merge');
const CopyPlugin = require('copy-webpack-plugin');
const webpack = require('webpack');
const baseConfig = require('./webpack.base.config.js');

const config = {
  // do not need a 'target' as this is for the browser

  entry: './src/client/client.jsx',

  output: {
    filename: 'client-bundle.js',
    path: path.resolve(__dirname, 'public'),
  },

  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: path.resolve(__dirname, 'src', 'styles'),
          to: path.resolve(__dirname, 'public'),
        },
      ],
    }),
    new webpack.DefinePlugin({
      'process.env.IS_BROWSER': true,
    }),
  ],
};

module.exports = merge(baseConfig, config);

The src/client/client.jsx contains

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { renderRoutes } from 'react-router-config';

import Routes from '../pages/Routes';

ReactDOM.hydrate(
  <BrowserRouter>
    <div>{renderRoutes(Routes)}</div>
  </BrowserRouter>,
  document.querySelector('#root'),
);

To build the app bundles

"build-server-bundle": "webpack --config webpack.server.config.js",
"build-client-bundle": "webpack --config webpack.client.config.js",

To run the app

node dist/server-bundle.js

The SSR application does not have "react-scripts" as it was not built with "create-react-app".

I was thinking it should be possible to create a client side bundle like "create-react-app" creates which can be then run on a browser only.

Does anyone know if this is possible and if so, how to do it.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
se22as
  • 2,282
  • 5
  • 32
  • 54

1 Answers1

0

You can check following repo for SSR and CSR both. https://github.com/ashish03399/react-js-ssr-csr-template

This template provide Server side rendering and client side rendering both with minimal required third party modules

User
  • 91
  • 1
  • 5