Deploy a React Monorepo to Firebase with Multiple Environments
Managing multiple React applications in a monorepo while maintaining separate staging and production environments can be challenging. In this guide, I'll show you exactly how to deploy a JavaScript monorepo with multiple React apps to Firebase, complete with proper environment separation and CI/CD integration.
The Setup: What We're Building
Let's say you have a monorepo containing multiple React applications managed by Lerna. In our example, we have:
- π± Frontend app - Main user-facing application
- βοΈ Admin app - Internal administration panel
- π¨ Design system app - Component library documentation
We need at least two environments:
- Staging - For testing and QA
- Production - For live users
We'll use separate Firebase projects for each environment, and within each project, we'll create different Firebase targets (sites) for each app in our monorepo.
Step-by-Step Firebase Configuration
1. Create Firebase Projects
For each environment, you'll need to:
- Create a Firebase project in the Firebase Console
- Initialize Firebase in your local repo using:
firebase init
2. Deploy Initial Content
This step is crucial and often overlooked:
firebase deploy
Deploy an empty index.html
file first. This step is essential because it activates the hosting service and allows you to create additional targets (sites) in your Firebase project.
3. Create Firebase Sites for Each App
For each app in your monorepo, create a separate site:
- Go to the Hosting section in your Firebase Console
- Click "Add another site"
- Create sites like:
myproject-app1-staging
-
myproject-app2-staging
myproject-app3-staging
Repeat this process for your production project.
π‘ Pro tip: While you can use the Firebase CLI to create sites, I recommend using the console to avoid accidentally overwriting your
.firebaserc
file.
Configuration Files
firebase.json - Define Your Targets
This file specifies how each target maps to your build folders:
{
"hosting": [
{
"target": "app1",
"public": "packages/app1/build",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
},
{
"target": "app2",
"public": "packages/app2/build",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
},
{
"target": "app3",
"public": "packages/app3/build",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
]
}
I've added rewrites
rules to ensure your React Router works properly in production.
.firebaserc - Environment and Target Mapping
This file maps your environments to Firebase projects and targets to specific sites:
{
"projects": {
"default": "myproject-staging",
"staging": "myproject-staging",
"prod": "myproject-prod"
},
"targets": {
"myproject-staging": {
"hosting": {
"app1": ["myproject-app1-staging"],
"app2": ["myproject-app2-staging"],
"app3": ["myproject-app3-staging"]
}
},
"myproject-prod": {
"hosting": {
"app1": ["myproject-app1"],
"app2": ["myproject-app2"],
"app3": ["myproject-app3"]
}
}
}
}
Deployment Commands
Once configured, deploying is straightforward:
# Deploy to staging
firebase deploy -P staging
# Deploy to production
firebase deploy -P prod
# Deploy specific targets only
firebase deploy -P staging --only hosting:app1
CI/CD Integration with GitLab
Here's a complete GitLab CI configuration that builds and deploys your monorepo:
image: node:18.12.0
stages:
- build
- deploy
variables:
# Disable source maps in CI for faster builds
GENERATE_SOURCEMAP: false
# Build stage - shared by both environments
.build_template: &build_template
stage: build
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- packages/*/node_modules/
script:
- yarn install --frozen-lockfile
- NODE_OPTIONS=--openssl-legacy-provider yarn build
artifacts:
paths:
- packages/*/build/
expire_in: 1 hour
# Staging deployment
deploy-staging:
<<: *build_template
stage: deploy
only:
- develop
environment:
name: staging
url: https://myproject-app1-staging.web.app
variables:
REACT_APP_API_URL: "https://api-staging.myproject.com"
REACT_APP_SENTRY_ENVIRONMENT: "staging"
after_script:
- npm install -g firebase-tools
- firebase deploy -P staging --token $FIREBASE_TOKEN
# Production deployment
deploy-production:
<<: *build_template
stage: deploy
only:
- main
environment:
name: production
url: https://myproject-app1.web.app
variables:
REACT_APP_API_URL: "https://api.myproject.com"
REACT_APP_SENTRY_ENVIRONMENT: "production"
after_script:
- npm install -g firebase-tools
- firebase deploy -P prod --token $FIREBASE_TOKEN
when: manual # Require manual approval for production
Setting Up Firebase Authentication
To get your FIREBASE_TOKEN
for CI/CD:
# Generate token (legacy method, but still works)
firebase login:ci
# Modern approach - use service account
firebase projects:list
β οΈ Security note: Store your
FIREBASE_TOKEN
as a protected variable in GitLab CI/CD settings, not in your code.
Common Gotchas and Solutions
1. "Project not initialized" Error
If you get this error when trying to deploy to staging, it means you skipped step 2. You need to:
- Delete your
firebase.json
- Run
firebase init
- Deploy an empty
index.html
- Create your sites in the console
- Restore your multi-target
firebase.json
2. Build Optimization
For faster CI builds, consider:
- Using
--frozen-lockfile
with yarn - Caching
node_modules
appropriately - Disabling source maps in CI (
GENERATE_SOURCEMAP=false
) - Using the legacy OpenSSL provider if needed
3. Environment Variables
Make sure your environment variables are set correctly for each environment in your CI/CD pipeline. React apps need variables prefixed with REACT_APP_
.
Benefits of This Approach
β
Clean separation between environments
β
Independent scaling for each app
β
Atomic deployments - deploy apps independently
β
Easy rollbacks - each app can be rolled back separately
β
Cost-effective - Firebase hosting is generous with free tier
What's Next?
This setup gives you a solid foundation, but you can enhance it further:
- Add preview deployments for pull requests
- Implement blue-green deployments
- Add automated testing before deployment
- Set up monitoring and alerting
- Configure custom domains for each app
Have you set up a similar monorepo deployment strategy? What challenges did you face? Share your experience in the comments below!
If you found this helpful, follow me for more DevOps and React content! π
Top comments (0)