What you need first

  • Node.js 22+ and pnpm installed locally.

  • A Firebase project (create one in the Firebase console).

  • Firebase CLI installed: npm install -g firebase-tools

Link the repo to your Firebase project

From the repository root, log in and connect the project.

firebase login
firebase use --add

This creates or updates .firebaserc with your default project id.

{
  "projects": {
    "default": "your-firebase-project-id"
  }
}

Configure Next.js for static export

Firebase Hosting serves static files. In packages/next-frontend/next.config.ts, enable static export and set the output folder to dist.

const nextConfig = {
  output: "export",
  distDir: "dist",
  trailingSlash: true,
};

Run pnpm --filter @22c-publishing-platform/next-frontend build and confirm the site is generated in packages/next-frontend/dist.

Configure Firebase hosting and deployment scripts

Add the hosting section to your root firebase.json.

{
  "firestore": {
    "rules": "firestore.rules",
    "indexes": "firestore.indexes.json"
  },
  "hosting": {
    "public": "packages/next-frontend/dist",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  },
  "storage": {
    "rules": "storage.rules"
  }
}

Add deploy scripts to the root package.json. The predeploy step builds shared packages before the frontend.

{
  "scripts": {
    "deploy": "firebase deploy",
    "predeploy": "pnpm --filter @22c-publishing-platform/component-library build &&\n  pnpm --filter @22c-publishing-platform/next-frontend build"
  }
}

Set environment variables for the frontend build

Create packages/next-frontend/.env with your Firebase web app config (Firebase console → Project settings → Your apps).

NEXT_PUBLIC_FIREBASE_API_KEY=
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=
NEXT_PUBLIC_FIREBASE_PROJECT_ID=
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=
NEXT_PUBLIC_FIREBASE_APP_ID=
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=

These values are baked into the static build, so set them before running pnpm run deploy.

Deploy Firestore, Storage rules, and indexes

Keep these files up to date in the repo root:

  • firestore.rules — database security rules

  • firestore.indexes.json — composite indexes used by queries

  • storage.rules — Cloud Storage security rules

firebase deploy publishes hosting, rules, and indexes together. To deploy only hosting during a quick UI check:

firebase deploy --only hosting

To deploy only rules or indexes:

firebase deploy --only firestore:rules,firestore:indexes,storage

Deploy locally

From the repository root:

pnpm install
pnpm run deploy

The predeploy script builds the component library and Next.js app, then Firebase CLI uploads the dist folder and applies rules.

When deployment finishes, open the Hosting URL shown in the terminal (or Firebase console → Hosting).

Set up the GitHub deployment pipeline

Add these repository secrets in GitHub (Settings → Secrets and variables → Actions):

FIREBASE_TOKEN=
NEXT_PUBLIC_FIREBASE_API_KEY=
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=
NEXT_PUBLIC_FIREBASE_PROJECT_ID=
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=
NEXT_PUBLIC_FIREBASE_APP_ID=
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=

Generate FIREBASE_TOKEN locally with:

firebase login:ci

Copy the token into the GitHub secret named FIREBASE_TOKEN.

Add the deployment workflow to .github/workflows/deploy.yml.

name: Deploy Firebase

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - name: Setup pnpm
        uses: pnpm/action-setup@v6
      - name: Setup Node
        uses: actions/setup-node@v6
        with:
          node-version: "22"
          cache: "pnpm"
      - name: Install Firebase CLI
        run: npm install -g firebase-tools
      - name: Install dependencies
        run: pnpm install
      - run: pnpm run deploy
        env:
          FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
          NEXT_PUBLIC_FIREBASE_API_KEY: ${{ secrets.NEXT_PUBLIC_FIREBASE_API_KEY }}
          NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN: ${{ secrets.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN }}
          NEXT_PUBLIC_FIREBASE_PROJECT_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_PROJECT_ID }}
          NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET: ${{ secrets.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET }}
          NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID }}
          NEXT_PUBLIC_FIREBASE_APP_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_APP_ID }}
          NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID }}
          NEXT_PUBLIC_GOOGLE_MAPS_API_KEY: ${{ secrets.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY }}

Push to main to trigger a production deploy. Check the Actions tab if a deploy fails.

After deployment

  • Open the live Hosting URL and smoke-test key pages.

  • In Firebase console, confirm Firestore indexes show as Enabled (new indexes can take a few minutes).

  • Re-run deploy whenever you change firestore.rules, storage.rules, or firestore.indexes.json.