Prerendering a create-react-app app

without ejecting or custom build steps

When you use create-react-app , by default it builds a blank HTML page.

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<meta name="viewport" content="width=device-width,initial-scale=1">

<link rel="shortcut icon" href="/favicon.ico">

<title>React App</title>

<link href="/static/css/main.dbf5d3f5.css" rel="stylesheet"></head>

<body>

<div id="root"></div>

<script type="text/javascript" src="/static/js/main.f4ad8021.js"></script>

</body>

</html>

This means when a user enters my app, they will see a blank page until the script is loaded.

I can write some splash screen logic into the template file ( public/index.html ), but that means more code for me to maintain.

One solution is to prerender the app’s initial markup into the template file, so that the user sees this while the script downloads.

This can be done easily for apps created using create-react-app .

First, I created an AppPrerenderer that exports a prerender function. This function returns the markup for my app’s initial state.

// src/AppPrerenderer.js

import AppContainer from './AppContainer'

import React from 'react'

import ReactDOMServer from 'react-dom/server' exports.prerender = function () {

return ReactDOMServer.renderToString(<AppContainer />)

}

Then I call that function from the template file, public/index.html :

<body>

<div id="root"><%=

require('../src/AppPrerenderer').prerender()

%></div>

</body>

This is possible because create-react-app uses html-webpack-plugin , which allows ejs templates.

Now, when the app is built, the app’s initial markup will be embedded inside the HTML file:

Caveats

Because we prerender the HTML file at build-time, AppPrerenderer.js must be runnable inside Node.js environment (where webpack is run).

This means you cannot load any CSS file, or you will get the “ ReferenceError: window is not defined ” error caused by code generated by style-loader .

I don’t have this problem because I use functional CSS and inline styles. See this issue for solutions and workarounds.