Free, robust and collaborative create-react-app deployments on Netlify

Mike Nason
Netlify
Published in
5 min readJan 8, 2018
Photo by Celso on Unsplash

I’ve been helping a startup bootstrap their frontend application infrastructure, and deployment has been swell with create-react-app and Netlify. When a change is pushed to the master branch, Netlify builds and deploys the app to production 🎉

The team at Facebook have really hit a home run with create-react-app for zero-config React applications. Similarly, the team at Netlify have made continuous delivery of static sites a breeze.

There were a few things left to be desired with this setup, though, especially as more people started collaborating on this project, and doubly so once customers started using it.

We needed to take this stack to the next level, and enable something like a delivery pipeline for individual features. I’m going to cover a few things we did to make development and release environments more robust and reliable in this post:

  1. Tagging deploys with build variables for better observability and error tracing
  2. Enabling preview deployments on pull requests, and dealing with complexities introduced by having multiple deployments
  3. Leveraging Netlify’s Continuous Delivery platform as Continuous Integration

Finally, this led to the creation of https://www.npmjs.com/package/build-create-react-app-netlify to help others benefit from this setup!

Tagging Netlify + create-react-app deploys

For now, we have no reason to eject from the baked in create-react-app setup. The zero-config nature provides everything we need. It’s really great!

By default create-react-app only injects environment variables that are prefixed with REACT_APP_ into the build. Netlify’s build variables, understandably, are not prefixed in this way.

By creating a custom build-create-react-app-netlify script, and configuring Netlify to run that instead of the default yarn build, we can work around this:

#!/usr/bin/env bashset -eecho 'Mapping Netlify build env vars for create-react-app, and running build ...'set -xREACT_APP_REPOSITORY_URL="$REPOSITORY_URL" \
REACT_APP_BRANCH="$BRANCH" \
REACT_APP_PULL_REQUEST="$PULL_REQUEST" \
REACT_APP_HEAD="$HEAD" \
REACT_APP_COMMIT_REF="$COMMIT_REF" \
REACT_APP_CONTEXT="$CONTEXT" \
REACT_APP_URL="$URL" \
REACT_APP_DEPLOY_URL="$DEPLOY_URL" \
REACT_APP_DEPLOY_PRIME_URL="$DEPLOY_PRIME_URL" \
yarn build

Note: this has been published on npm as https://www.npmjs.com/package/build-create-react-app-netlify

The application’s instrumentation and error reporting immediately benefited from this change. For example, we now use these variables to log our errors with the git hash and branch that triggered that build, allowing us to reliably monitor and trace errors across multiple deployments.

See Netlify’s docs on build environment variables for more details on what each of these mean.

Preview Deployments and Complexity

Another goal in this process was to enable preview deployments on pull requests. Netlify makes this trivial, but can only offer so much in terms of managing the complexity of multiple deployments.

For example, this app uses Auth0 for authentication. Auth0 needs a callback URL to work, and we had no way of reliably knowing the current deployment’s URL.

Different deployments = different URLs, and we had no way of reliably knowing the current deployment’s URL.

Out of the box, this led to deployments that were unable to authenticate, so we couldn’t log in or use these preview apps in any meaningful way.

Once we implemented the build-create-react-app-netlify script above, we made use of process.env.REACT_APP_DEPLOY_PRIME_URL to set the dynamic callback URL, and to deploy usable previews from each pull request.

Note for Auth0 users: you’ll also need to add something like https://*--your-app-name.netlify.com to your “Allowed Callback URLs” for this to work.

Configuring multiple deployments

We checked-in a custom netlify.toml to declare context-specific environment variables and overrides for different types of deployments. I’m comfortable checking these in since environment variables get exposed in the public output of the build, and aren’t terribly secret for this static frontend.

It looks something like:

# Global settings applied to the whole site.[build]
publish = "build"
command = "./scripts/netlify-build"
# PRODUCTION context: All deploys to the main
# repository branch will inherit these settings
[context.production.environment]
REACT_APP_ENV_LABEL = "production"
REACT_APP_API_URL = "https://api.example.com"
REACT_APP_S3_BUCKET = "ex_production"
REACT_APP_AUTH0_CALLBACK_URL = "https://portal.example.com/callback"
REACT_APP_SOME_KEY = "abcd1234"
# Deploy Preview context: All Deploy Previews
# will inherit these settings.
[context.deploy-preview.environment]
REACT_APP_ENV_LABEL = "review"
REACT_APP_API_URL = "https://api-staging.example.com"
REACT_APP_S3_BUCKET = "ex_staging"
# Branch Deploy context: All deploys that are not in
# an active Deploy Preview will inherit these settings
[context.branch-deploy.environment]
REACT_APP_ENV_LABEL = "review"
REACT_APP_API_URL = "https://api-staging.example.com"
REACT_APP_S3_BUCKET = "ex_staging"
# Specific branch context: Deploys from a branch
# will merge matching config into the preview
# or branch deploy config.
[context."feat/testing-something-new".environment]
REACT_APP_NEW_VAR = "o"
[context.staging.environment]
REACT_APP_ENV_LABEL = "staging"
REACT_APP_AUTH0_CALLBACK_URL = "https://portal-staging.example.com/callback"

See Netlify’s docs on Deploy Contexts for more detail on the netlify.toml file.

We also use Netlify’s UI to declare global environment variables, or ones that we might want to change without touching code.

Netlify as Continuous Integration

We wanted to leverage the tests we had been writing to validate each build. Netlify is a fantastic continous delivery tool, but it was not clear if we could use it for continuous integration in this way.

Fortunately, Netlify’s infrastructure makes this mostly painless (more on this below). By adding another command to the top of the build-create-react-app-netlify script, we run tests first and only proceed with a build if they are successful:

#!/usr/bin/env bashset -eecho 'Running tests ...'
yarn install --production=false && CI=true yarn test
...

And with that, failing tests in a branch will be reported correctly and halt the deployment.

A few pain-points/notes — Netlify installs node dependencies in production mode. This means that any dev dependencies, including create-react-app's dev dependencies are not installed. This happens to be where most test tooling is installed.

So, the first part of this command does another install, but includes all dependencies with the --production=false flag. This allows us to run tests, but can add up to 1–2 minutes to the build.

Then, setting CI=true for create-react-app’s test command ensures that tests are run once and then exit. Without this, the test suite enters watch mode and will cause the build to time out.

Conclusion

I think this is an important infrastructure improvement that will help the team iterate and collaborate effectively on this project.

In the next few days, I hope to publish the build-create-react-app-netlify script on npm, so that anyone can benefit from the same setup with ease.

Update: This is now available at https://www.npmjs.com/package/build-create-react-app-netlify

If you’re using Netlify and create-react-app, I hope this helps you towards a more reliable and observable application. Feel free to reach out with questions or feedback. And if you’ve solved these problems in different ways, I’d love to hear more about it!

--

--