Heroku Buildpack for create-react-app
Deploy React.js web apps generated with create-react-app. Automates deployment with the built-in bundler and serves it up via Nginx. See the introductory blog post and entry in Heroku elements.
🚦 Purpose⚠️ Requirements- 🚀 Quick Start
🛠 Usage👓 Customization🕵️ Troubleshooting- 📍 Version compatibility
🏙 Architecture
Purpose
This buildpack deploys a React UI as a static web site. The Nginx web server provides optimum performance and security for the runtime. See Architecture for details.
If your goal is to make a single app that combines React UI with a server-side backend (Node, Ruby, Python…), then this buildpack is not the answer.
Check out these alternatives to use React with a server-side app:
- create-react-app + Node.js server
⭐️ simplest solution - create-react-app + Ruby on Rails server
Requires
Quick Start
Ensure requirements are met, then execute the following in a terminal.
$APP_NAME
with the name for your unique app.
npx create-react-app@3.x $APP_NAME
cd $APP_NAME
heroku create $APP_NAME --buildpack mars/create-react-app
git push heroku master
heroku open
Once deployed, continue development
For explanation about these steps, continue reading the next section.
Usage
Generate a React app
$APP_NAME
with the name for your unique app.
npx create-react-app@3.x $APP_NAME
cd $APP_NAME
- as of create-react-app v3, it automatically performs
git init
and an initial commit - npx comes with npm 5.2+ and higher, see instructions for older npm versions
- if yarn is installed locally, the new app will use it instead of npm
Create the Heroku app
✏️ Replace $APP_NAME
with the name for your unique app.
heroku create $APP_NAME --buildpack mars/create-react-app
This command:
- sets the app name & its default URL
https://$APP_NAME.herokuapp.com
- sets the app to use this buildpack
- configures the
heroku
git remote in the local repo, sogit push heroku master
will push to this new Heroku app.
♻️
Deploy git push heroku master
…or if you are ever working on a branch other than master
:
✏️ Replace $BRANCH_NAME
with the name for the current branch.
git push heroku $BRANCH_NAME:master
Visit the app's public URL in your browser
heroku open
Visit the Heroku Dashboard for the app
Find the app on your dashboard.
Continue Development
Work with your app locally using npm start
. See: create-react-app docs
Then, git commit
your changes & git push heroku master
♻️
Push to Github
Eventually, to share, collaborate, or simply back-up your code, create an empty repo at Github, and then follow the instructions shown on the repo to push an existing repository from the command line.
Testing
Use create-react-app's built-in Jest testing or whatever testing library you prefer.
Heroku CI is supported with minimal configuration. The CI integration is compatible with npm & yarn (see bin/test
).
app.json
Minimal Heroku CI uses app.json
to provision test apps. To support Heroku CI, commit this minimal example app.json
:
{
"buildpacks": [
{
"url": "mars/create-react-app"
}
]
}
Customization
Procfile
Heroku apps may declare what processes are launched for a successful deployment by way of the Procfile
. This buildpack's default process comes from heroku/static
buildpack. (See: Procfile
to start the static web server is:
web: bin/boot
To customize an app's processes, commit a Procfile
and deploy. Include web: bin/boot
to launch the default web process, or you may replace the default web process. Additional process types may be added to run any number of dynos with whatever arbitrary commands you want, and scale each independently.
Web server
The web server may be configured via the static buildpack.
The config file static.json
should be committed at the root of the repo. It will not be recognized, if this file in a sub-directory
The default static.json
, if it does not exist in the repo, is:
{
"root": "build/",
"routes": {
"/**": "index.html"
}
}
Changing the root
If a different web server "root"
is specified, such as with a highly customized, ejected create-react-app project, then the new bundle location may need to be set to enable runtime environment variables.
Routing
HTTPS-only
Enforce secure connections by automatically redirecting insecure requests to https://, in static.json
:
{
"root": "build/",
"routes": {
"/**": "index.html"
},
"https_only": true
}
Strict transport security (HSTS)
Prevent downgrade attacks with HTTP strict transport security. Add HSTS "headers"
to static.json
.
herokuapp.com
hostnames are safe to use with HSTS.
{
"root": "build/",
"routes": {
"/**": "index.html"
},
"https_only": true,
"headers": {
"/**": {
"Strict-Transport-Security": "max-age=31557600"
}
}
}
max-age
is the number of seconds to enforce HTTPS since the last connection; the example is one-year
Proxy
Proxy XHR requests from the React UI in the browser to API backends. Use to prevent same-origin errors when CORS is not supported on the backend.
Proxy URL prefix
To make calls through the proxy, use relative URL's in the React app which will be proxied to the configured target URL. For the example URL prefix of /api/
, here's how the proxy would rewrite the requests:
/api/search-items
→ https://backend.example.com/search-items
/api/users/me
→ https://backend.example.com/users/me
You may choose any prefix and may have multiple proxies with different prefixes.
Proxy for deployment
The heroku/static
buildpack (see:
Add "proxies"
to static.json
:
{
"root": "build/",
"routes": {
"/**": "index.html"
},
"proxies": {
"/api/": {
"origin": "${API_URL}"
}
}
}
Then, point the React UI app to a specific backend API:
heroku config:set API_URL="https://backend.example.com"
Proxy for local development
create-react-app itself provides a built-in proxy for development. This may be configured to match the behavior of proxy for deployment.
Add "proxy"
to package.json
:
{
"proxy": {
"/api": {
"target": "http://localhost:8000",
"pathRewrite": {
"^/api": "/"
}
}
}
}
Replace http://localhost:8000
with the URL to your local or remote backend service.
Environment variables
REACT_APP_*
environment variables are fully supported with this buildpack.
Set vars on Heroku
heroku config:set REACT_APP_HELLO='I love sushi!'
Set vars for local dev
Requires at least create-react-app 0.7. Earlier versions only support Compile-time.
Create a .env
file that sets a variable per line:
REACT_APP_API_URL=http://api.example.com
REACT_APP_CLIENT_ID=XyzxYzxyZ
Compile-time vs Runtime
Two versions of variables are supported. In addition to compile-time variables applied during build the app supports variables set at runtime, applied as each web dyno starts-up.
Requirement | Compile-time | Runtime |
---|---|---|
never changes for a build | ✓ | |
support for continuous delivery | ✓ | |
updates immediately when setting new config vars | ✓ | |
different values for staging & production (in a pipeline) | ✓ | |
ex: REACT_APP_BUILD_VERSION (static fact about the bundle) |
✓ | |
ex: REACT_APP_DEBUG_ASSERTIONS (prune code from bundle) |
✓ | |
ex: REACT_APP_API_URL (transient, external reference) |
✓ | |
ex: REACT_APP_FILEPICKER_API_KEY (Add-on config vars) |
✓ |
Compile-time configuration
Supports all config vars, including REACT_APP_
, NODE_
, NPM_
, & HEROKU_
prefixed variables.
REACT_APP_
vars, then they may be accessed by anyone who can see the React app.
Use Node's process.env
object.
import React, { Component } from 'react';
class App extends Component {
render() {
return (
<code>Runtime env var example: { process.env.REACT_APP_HELLO }</code>
);
}
}
heroku config:set REACT_APP_HELLO='I love sushi!'
git commit --allow-empty -m "Set REACT_APP_HELLO config var"
git push heroku master
Only REACT_APP_
vars are replaced in create-react-app's build. To make any other variables visible to React, they must be prefixed for the build command in package.json
, like this:
REACT_APP_HEROKU_SLUG_COMMIT=$HEROKU_SLUG_COMMIT react-scripts build
Runtime configuration
Supports only REACT_APP_
prefixed variables.
Install the runtime env npm package:
npm install @mars/heroku-js-runtime-env --save
Then, require/import it to use the vars within components:
import React, { Component } from 'react';
import runtimeEnv from '@mars/heroku-js-runtime-env';
class App extends Component {
render() {
// Load the env object.
const env = runtimeEnv();
// …then use values just like `process.env`
return (
<code>Runtime env var example: { env.REACT_APP_HELLO }</code>
);
}
}
\n
, into Runtime config vars. Use literal UTF-8 values only; they will be automatically escaped.
Custom bundle location
If the javascript bundle location is customized, such as with an ejected created-react-app project, then the runtime may not be able to locate the bundle to inject runtime variables.
To solve this so the runtime can locate the bundle, set the custom bundle path:
heroku config:set JS_RUNTIME_TARGET_BUNDLE=/app/my/custom/path/js/*.js
*
glob, selecting multiple files, because as of create-react-app version 2 the bundle is split.
To unset this config and use the default path for create-react-app's bundle, /app/build/static/js/*.js
:
heroku config:unset JS_RUNTIME_TARGET_BUNDLE
Add-on config vars
🚫🤐 Be careful not to export secrets. These values may be accessed by anyone who can see the React app.
Use a custom .profile.d
script to make variables set by other components available to the React app by prefixing them with REACT_APP_
.
-
create
.profile.d/000-react-app-exports.sh
-
make it executable
chmod +x .profile.d/000-react-app-exports.sh
-
add an
export
line for each variable:export REACT_APP_ADDON_CONFIG=${ADDON_CONFIG:-}
-
set-up & use Runtime configuration to access the variables
For example, to use the API key for the Filestack JS image uploader:
export REACT_APP_FILEPICKER_API_KEY=${FILEPICKER_API_KEY:-}
npm Private Packages
Private modules are supported during build.
-
Setup your app with a
.npmrc
file following npm's guide for CI/deployment. -
Set your secret in the
NPM_TOKEN
config var:heroku config:set NPM_TOKEN=xxxxx
Troubleshooting
-
Confirm that your app is using this buildpack:
heroku buildpacks
If it's not using
create-react-app-buildpack
, then set it:heroku buildpacks:set mars/create-react-app
…and deploy with the new buildpack:
git commit --allow-empty -m 'Switch to create-react-app-buildpack' git push heroku master
If the error still occurs, then at least we know it's really using this buildpack! Proceed with troubleshooting.
-
Check this README to see if it already mentions the issue.
-
Search our issues to see if someone else has experienced the same problem.
-
Search the internet for mentions of the error message and its subject module, e.g.
ENOENT "node-sass"
-
File a new issue. Please include:
- build log output
- link to GitHub repo with the source code (if private, grant read access to @mars)
Version compatibility
This buildpack will never intentionally cause previously deployed apps to become undeployable. Using master as directed in the main instructions will always deploy an app with the most recent version of this buildpack.
Releases are tagged, so you can lock an app to a specific version, if that kind of determinism pleases you:
heroku buildpacks:set https://github.com/mars/create-react-app-buildpack.git#v6.0.0
v6.0.0
with the desired release tag.
🏙
Architecture This buildpack combines several buildpacks, specified in .buildpacks
, to support zero-configuration deployment on Heroku:
heroku/nodejs
buildpack- installs
node
, puts on the$PATH
- version specified in
package.json
,engines.node
node_modules/
cached between deployments- production build for create-react-app
- executes the npm package's build script; create-react-app default is
react-scripts build
- exposes all env vars to the build script
- generates a production bundle regardless of
NODE_ENV
setting - customize further with Node.js build configuration
- executes the npm package's build script; create-react-app default is
- installs
mars/create-react-app-inner-buildpack
- sets default web server config unless
static.json
already exists - enables runtime environment variables
- sets default web server config unless
heroku/static
buildpack- Nginx web server
- configure with
static.json
(see also all static web server config)
web
process is the last buildpack's default processes. heroku-buildpack-static uses bin/boot
to launch its Nginx web server. Processes may be customized by committing a Procfile to the app.
General-purpose SPA deployment
Some kind feedback pointed out that this buildpack is not necessarily specific to create-react-app
.
This buildpack can deploy any SPA [single-page app] as long as it meets the following requirements:
npm run build
performs the transpile/bundling- the file
build/index.html
or the root specified instatic.json
exists at runtime.