Demonstration of how to configure SSR for BOTH DEV AND PROD after ejecting with create-react-app
â Updated for Create React App v3.4.1
This repository was bootstrapped with Create React App, ejected, then configured to perform Server Side Rendering (SSR).
If youâre interested in SSR then youâd be aware that Create React App has no plans to include support for it:
Ultimately server side rendering is very hard to add in a meaningful way without also taking opinionated decisions. We donât intend to make such decisions at this time.â ââDan Abramov
This repository, then, is my minimally-opinionated SSR configuration, sticking closely to Create React Appâs style and introducing as few new things (packages, code, configuration) as possible.
yarn
to update dependencies;yarn start
or yarn build
as normal.Note: the diff and patch links here compare the HEAD of the master branch to the commit immediately after ejecting, so they will stay up to date if/when new commits are pushed to this repo.
I recommend applying the changes by hand, rather than forking or otherwise copying this repo, so that you benefit from the latest version of Create React App (and to see first-hand what the configuration changes are!).
If you use the patch, be sure to review the changes after applying, to make sure it all makes sense with the current version of Create React Appâs scripts and configuration.
To run in production, run yarn build
then deploy at least the following files and folders:
build/
config/
scripts/
package.json
yarn.lock
If the serverâs environment has NODE_ENV=production
set, then run yarn install
to install dependencies then use
node scripts/start.js
(or yarn start
) to launch the server process (which youâll probably want to do via tools like
Nodemon and pm2).
If your server doesnât already have NODE_ENV=production
set in its environment, then use yarn install --prod
and
NODE_ENV=production node scripts/start.js
(or yarn start:prod
) instead.
Note: the dependencies in
package.json
have been split into devDependencies and normal runtime dependencies, so runningyarn install --prod
(or running withNODE_ENV=production
) will not install any libraries that arenât needed in a production environment. That mostly means the webpack infrastructure.
README.md
(the file youâre reading now).editorconfig
(although I do recommend using one)package.json
(use yarn add
to add the new dependencies so you get the current versions)yarn.lock
(yours will be regenerated when you run yarn
)yarn start
is used for both the local dev server (the default) and in production;index.html
is removed, and the html received by the browser is constructed instead by the expresssrc/server/index.js
(including pre-rendering the React component tree on the server) in both local devReactDOM.render()
is changed to ReactDOM.hydrate()
;yarn build
is used (as usual) to prepare a build for production, and must be run before yarn start
can be usedsrc/server/
.Note: As with Create React App, only CSS changes are hot-reloaded without a browser refresh. If youâre interested in improving your hot-module-replacement experience, try vanilla webpack HMR first, especially if you donât need to preserve component state (e.g. you use Redux). Thatâs simply a matter of:
- Run
yarn add -D webpack-hot-middleware
. (This is required because create-react-appâs HMR client doesnât
understand multiple compiler configurations, so forces a full refresh on every code update).- Add the below code to
src/index.js
:
module.hot.accept('./App', () => {
ReactDOM.render(<App />, document.getElementById('root'));
});
- In
server.dev.js
, addconst webpackHotMiddleware = require('webpack-hot-middleware');
at the top,
andapp.use(webpackHotMiddleware(compiler.compilers[0]));
as the first line ofserverConfig.after()
.If thatâs not enough for your use case, and you are brave, try react-hot-loader.
webpack.config.js
is changed to export an [array] of configurations, containing configuration for the clientclient
and server
, which is a requirement of webpack-hot-server-middleware;index.html
file is deleted;config/webpack.config.server.common.js
and mostly involve disabling tasks that donât need toprocess.env
settings into the bundle.config/webpackDevServer.config.js
src/server/
;scripts/start.js
The code that is specific to local development has been moved into scripts/server.dev.js
, so that start.js
can be
used to start the server in production as well.
scripts/server.dev.js
Initializes the Webpack Dev Server. Unfortunately I couldnât find a good way to link to a visual diff between the
original start.js
and the new server.dev.js
but if you use a tool of your own, such as the compare function in an
IDE, youâll see that it is made up of fragments from the original start.js
with only a few additions:
after
hook to mount the webpack-hot-server-middleware, and the same error handling middleware thatscripts/server.prod.js
This file is all new, and basically it initializes an Express server to:
src/server/index.js
.It also includes a quick check that youâve run yarn build
, and if the assets arenât found then it attempts to run the
build script itself before proceeding.
Note: This automatic build wonât work if
yarn install
was executed in an environment whereNODE_ENV
was set toproduction
, because the webpack infrastructure wonât be present.
scripts/build.js
index.html
;src/server/*
This code is all new, with the most interesting work happening in src/server/middleware/render.js
. Thatâs where the
html is constructed by:
<link>
tags for any CSS assets output by the build (code adapted from html-webpack-plugin);<script>
tag for the main client bundle;<div>
with the output from renderToString()
.Youâll want to add things like prerendered Redux state, or prerendered CSS from a CSS-in-JS technology like JSS, here.
During local development, you will see a FOUC (Flash Of Unstyled Content) between the browserâs initial paint and when the client Javascript inserts the CSS into the DOM. This does not occur in production.
The FOUC is inherent to the CSS configuration adopted by Create React App (i.e. the use of style-loader to support hot module replacement during development), but it isnât an issue without SSR because without SSR you canât see anything at all until the client Javascript has executed!
Personally, in my projects I delete the CSS configuration and use JSS instead. When using JSS and Redux, hot reloading is clean and seamless.
Many thanks to @faceyspacey for universal-demo, which introduced me to webpack-hot-server-middleware and the idea of using multiple Webpack configurations to build the client and server code at the same time đ.