In this article, we’ll cover & compare how to deploy React Apps on different Cloud Hosting providers. We’ll take you from start to finish. Including initializing your React App, signing up for the Cloud Hosting provider and deploying your React App.
We’ll be updating this article periodically. Below, is a list of the providers we plan on covering and the status on each.
We take this "Evergreen" approach on some of our articles, in order to create ever-green pages and allow ourselves to pump out content right away. Instead of waiting till everything is perfect and never updating an article again, we want our articles to be a living, breathing piece of content that you can refer back to. With our evergreen articles, we have separate email lists (per article) to keep up-to-date. By subscribing below, you will only receive emails when this article is updated.
Deploying to Firebase has never been easier. Here, we’ll cover how to automate your workflow, in order to test & deploy to multiple environments. This will assist in rapid iterations to your app, without constantly pushing breaking changes to production. The goal is to have an environment for your users (production), an environment to approve changes prior to shipping (staging) and an environment that’s actively being worked on (development).
By the end of this article, your app will automatically be tested & deployed to a specific Firebase Project (one per environment) simply by pushing to GitHub. Although we go extremely in-depth in this article, the end result is rather simple. The commits can be seen here and are kept to one commit per step.
For the sake of this article, we’ll assume you’re unfamiliar with a few terms.
CI/CD: Continuous Integration is the practice of merging all outstanding changes in a codebase, as often as possible. Being sure to test at all points. Continuous deployment is the practice of automating deployments to your users. Getting them new updates regularly.
Firebase: A mobile and web application development platform, owned by Google, that provides a number of products for any real world app. In this article, we’ll only be using “Firebase Hosting”.
Github Actions: Allows you to create different workflows in order to automate certain tasks, such as CI/CD.
GitFlow: A widely adapted structure for naming your git branches.
There are a fewarticles on deploying a React App to Firebase, using GitHub Actions. This article aims to build upon that, adding multi-environment deployment. In addition, we opted not to use any pre-existing GitHub Actions for Firebase, in order to reduce the level of abstraction and allow you to see exactly what’s happening.
Firebase Projects set up. You will need one project per environment. (IE: App Dev, App Staging, App Production)
# Which Firebase CLI features do you want to set up for this folder?
# Firebase Hosting
# Select a default Firebase project for this directory
# (recommend you use your "dev" project for now, but any is fine)
# What do you want to use as your public directory?
# Configure as a single-page app (rewrite all urls to /index.html)?
# File public/index.html already exists. Overwrite?
Now, merge the .gitignore file Firebase uses with the one React automatically created. You can find the file here. Just copy and append it to the one you have locally. This is needed, at the time of writing, since Firebase doesn’t automatically overwrite your current .gitignore.
First, you’ll need to generate a Firebase CI token. This is needed in order for GitHub to authenticate to Firebase. Allowing you to deploy.
Caution: this token can be used to perform almost any task on any Firebase project you have access to. Save it. If, for any reason, your token becomes compromised, you can run the following command: firebase logout --token YOUR_TOKEN
Now, you’ll need to add your Firebase token to your repo’s secrets. Navigate to your GitHub repo, go to “Settings -> Secrets -> Add a New Secret”.
Note: Alternatively, you can name this FIREBASE_TOKEN, if you want to avoid passing the token explicitly during the deployment. more here.
Now that we’re all set up, let’s create our initial workflow. Create a file at .github/workflows/firebase_web.yml, with the contents below.
Curious as to what’s happening? We’re creating a workflow that’s triggered whenever a push is made. The workflow runs on Ubuntu. We checkout the current branch, this way the code we’re working on is the one that’s executed. Then, we install Node, which is needed by Firebase. After that, we run yarn (npm can also be used just fine) to install dependencies. Before deploying, it’s always best to test. We do that with the same test script React ships with. Only this time, we make sure to specify `CI=true`, which tells the React script that we’re working in a [CI environment](https://create-react-app.dev/docs/running-tests/#continuous-integration). From there, we run the typical “build” command that React ships with. After the build is complete, we deploy using Firebase CLI. We explicitly pass in our token (for authentication) & a message. The message consists of the GitHub commit message and a link to the workflow ran. This message will show in the Firebase console and will be useful, in knowing which commit triggered the current deployment.
Now, add firebase-tools as a dev dependency in your package.json
And if we head on over to Firebase Console -> Hosting, we’ll see our latest deployment along with the message we configured ⚡️
In Steps 1–3, we developed a workflow which is capable of automating our testing & deployment. If we were only concerned about one environment, our work would be done. Next, we’ll cover extending that workflow to multiple environments. We’ll follow the common pattern of 3 environments: development, staging, production.
This is the approach we recommend, as we enjoy the structure GitFlow provides. However, it does come with the added overhead of your team understanding GitFlow. If you’re not comfortable with this, feel free to use the simpler Option B below.
Here’s an outline of which branches we’d like to deploy to which environments, following GitFlow conventions.
║ Branch ║ Environment to Deploy ║ ║
║ feature/**, develop ║ dev ║ ║
║ release/** ║ staging ║ ║
║ master ║ production ║ ║
If you haven’t already, initialize GitFlow. Then, start a feature branch.
git flow init
## We kept all default options.
git flow feature start multi-environment-support
Update your workflow (firebase_web.yml) to utilize GitFlow style branches.
Let’s edit our .firebaserc to include our 3 project aliases, instead of just a “default”. This allows us to specify the project during deployment, via the -P argument.
Let’s say you wanted variables that vary depending on the environment they’re in (environment variables). This can be useful for things such as API keys, enabling/disabling features and more. React automatically configures any environment variables prefixed with REACT_APP_during build time.
To add environment variables to your React App, simply specify them in your workflow. Since you want the environment variables to be different for each environment, you’ll want to define them per deployment step.
For example, edit firebase_web.yml to include the following:
- name: Build & Deploy Dev
## Configure environment variables (do this on each build step)
REACT_APP_NAME: My App - Development
These variables will now be accessible to Github. You’ll likely want them accessible locally as well. React utilizes .env files for this.
Add .env.local to the root of your app, with the following contents
REACT_APP_NAME=My App - Local
Note: .env files are already ignored in .gitignore, so we don’t have to worry about them accidentally being committed and overwriting our CI/CD steps.
Environment variables can be accessed in a React App via process.env. For example, let’s try using the one we just set up.