Deploy production build create-react-app to Heroku

Create-react-app makes it easy to get started on a React project. I can start progress on an app without needing to worry first about development servers or webpack configuration.

It’s also incredibly easy to deploy a React app to Heroku. Simply push project code to a Heroku project and it runs its magic. Site deployed.

The problem: Deploying a production build of create-react-app to Heroku takes extra configuration to serve production files instead of code meant to be used just for development.

By the end of this I’ll have created a custom Express.js server and directed Heroku to use that instead of a create-react-app's development server. But first, a review of what's going on and the problems that need to be solved.

 

Default Heroku deploy process

If your project includes a package.json file, Heroku automatically defines it as a site that should run on a NodeJS server. It will deploy the app using a Node buildpack. Heroku looks to the package.json file for a start script and starts the web process by running npm start. Whatever that start script does is what your live project will be.

In a default create-react-app, the start script is: react-scripts start. This script is intended for use with local development. You can look through that script file in the react-scripts module to trace everything it does. The most important issue here is that it starts a Webpack dev server to handle serving the site files from the src directory. That’s great for local development, but no good for production. You don’t want hot reloading or non-minified files on production.

If Heroku starts the app by running npm start it will be running the development version of the site on live. So don’t rely on default Heroku deploy settings.

 

Default create-react-app build

Create-react-app has a production build script it its default package.json: react-scripts build The build script prepares a react project for production deployment. It does things like process and minify css and bundles the react app into a minified javascript file. The build process creates a new directory named build and puts all these new project files there. In local development, Webpack dev server looks to the src directory, so if Heroku is running the Webpack server it’s serving our project from src when it should be using build.

 

These defaults introduce two problems for deploying production code to Heroku.

  • There’s no server defined for production deployment. Webpack dev server isn’t present in the production build.
  • Production files are in a directory titled build that Heroku doesn’t know anything about.

 

Let's fix these problems.

Create an Express server

Configuring a simple Express server will fix the two problems above and will cover deployment of a lot of smaller React sites.

Keep in mind, if project is more complex this may not cover your needs. But if that’s the case, you probably would have solved those issues prior to this point :)

 

First! Include Express in the project.

Express will work on your create-react-app project without this step because it’s a sub-requirement of another node module in the project. But it should be explicitly required to avoid any future headaches.

From project root:

npm install express --save

 

Create the server file

Create a new file in the project root named server.js.

Open this new file in your code editor and add the following:

const express = require('express');
const path = require('path');
const app = express();
const PORT = process.env.PORT || 5000;

app.use(express.static(path.join(__dirname, 'build')));

app.get('*', function (req, res) {
 res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

app.listen(PORT);

 

There are a few very important things going on in this server code.

  • An Express.js server has been initiated
  • A variable listening port is set using Heroku’s process environment settings (<code>process.env.PORT</code>)
  • The build directory has been set as the project directory
  • All server requests are directed to “build/index.html” for a response


Tell Heroku to use a custom Express.js server

This can be handled in a couple ways, but I'll do it here using a procfile.

Create a procfile that will initiate the web process using the server.js file that was just created.
Even before Heroku gets to the packages.json file during a build/deploy process, it looks for a procfile in the project root. This file allows a project maintainer to configure processes. In this case, the web process needs to change to use the new server.js just created.

In the project root, create a new file named: procfile (no extension)

In a code editor, add the following to the procfile:

web node server.js

Remember, Heroku automatically builds sites that have a packages.json file as a node.js sites (using the default nodejs buidpack). To kick-off an Express server in a node.js environment the command is to simply use node to run our server.js file: node server.js. We just define the Heroku process, in this case, it’s the web process.

Now commit all these changes to the project repo and push to Heroku. The project will run on the new server and the files will be served from the build directory.