Inspiration for doing this -
We did not have Hot Module Replacement enabled for our development environment. Due to this every-time we made a small change, we had to refresh the page to see those changes.   You can imagine the cumulative wastage of developer time that happened due to this. Its a little tricky to enable HMR is a server side rendered app, because you have to somehow make webpack-dev-server talk to your web server, which is doing the server side rendering.

Requirement —

  1. Pages should be initially rendered at server side using some templating engine like EJS and some initial data injected into the page by the server.
  2. Express should be used as the web server, along with all the backend logic, and use that to render the views on initial page load.
  3. React code should be separated, and whenever changes are made to react components code/css, ui should be updated on the fly, without refreshing the page. read more about HMR

Dependencies

ejs: used for server side templating
express: our server
react, react-dom: for rendering react components

Dev Dependencies
Babel stuff: to transpile our JSX to javascript
loaders: loaders for webpack to resolve different module types.
@pmmmwh/react-refresh-webpack-plugin: webpack plugin to enable HMR for react components, without loosing state.
webpack stuff: to bundle our app and enable hot module replacement

Webpack Config

Nothing much different here. Webpack config is same as you would use for client side rendered app, powered by webpack dev server. Make sure that the entry points to the index.js where you have rendered your react root using ReactDOM.render

Client Side react code —

Nothing different here too. Just normal client side react code.

Server Side code

This is the interesting part. The problem in hot reloading a server side rendered app is that we have no way to connect it to the webpack-dev-server which enables HMR! because we are running it inside an express server, which knows nothing about webpack. here comes to our rescue webpack-dev-middleware and webpack-hot-middleware. These middlewares stick to the express app and enable the hot reloading.

Notice line 3–5 and 12–16. We import these modules and put them as normal middlewares of express. Also note that we pass the webpack config as argument to webpack [line 8] and then pass the compiler instance to these middlewares. [lines 12 and 16]
server.js

a sample view

notice on line 49, we include the bundle.js. This will be updated by webpack using the setup we did. Rest of the code is rendered server side. We can put whatever logic we want here. Notice line 41 and 44. These are dynamic values injected into ejs view by express server. See line 20 in server.js above. These can be any data processed or fetched at server side and injected into the ejs view. We can also use this data to hydrate the initial state of redux store. Say to validate the cookie and authenticate the user etc. We can also use this to optimize the critical rendering path, say inlining css for above the fold content, rendering above the fold content etc.

Thats it. You are good to go. Just run node index.js and your app should be up and running with hot module replacement enabled.

What did we achieve?

Changes now reflect instantly whether it be a CSS change to some change in component logic. No need to refresh and do the initial steps to get to the application state you are making changes in, saving countless developer minutes.
We can now invest our time and focus on what matters. The flow isn't broken in between.

The complete code is available at https://github.com/Vedantu/ssr-hot-reloading