Free, robust and collaborative create-react-app deployments on Netlify
By: Mike Nason
Tags: Continuous Delivery, Netlify, React, Monitoring, Continuous Integration

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 bash set -e echo 'Mapping Netlify build env vars for create-react-app, and running build ...' set -x REACT_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 bash set -e echo '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.