Acme Store Docs
Features

Deployment

Deploy to Vercel with the right environment variables and production settings.

The boilerplate works out of the box on Vercel: import the repo, set the environment variables from .env.example, and deploy. Any Node host that supports Next.js 14 works too. The store is quote-based by default — Stripe payments, Resend emails, and the SQL backends are all optional and env-gated, so a first deploy needs nothing more than your Firebase web credentials and a sign-in redirect URL.

A default (Firebase) build succeeds with no credentials at all: src/lib/firebase/config.ts falls back to demo values so the SDK initializes during prerender. You still need real env vars at runtime for the deployed store to talk to your own Firebase project.

Deploy to Vercel

Import the repository

In the Vercel dashboard, Add New → Project and import your fork/clone. Vercel auto-detects Next.js 14 (App Router) — the default build command next build and output settings need no changes.

Set the environment variables

Open Project Settings → Environment Variables and add every value your deployment needs from .env.example. For a default quote-based store, the Firebase web credentials plus the sign-in redirect URL are the only required entries:

VariableRequiredNotes
NEXT_PUBLIC_FIREBASE_API_KEYYesFirebase web app config
NEXT_PUBLIC_FIREBASE_AUTH_DOMAINYese.g. your-project.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_IDYese.g. your-project
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKETYes*Required for product image uploads / media library
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_IDYesFirebase web app config
NEXT_PUBLIC_FIREBASE_APP_IDYesFirebase web app config
NEXT_PUBLIC_FIREBASE_MEASUREMENT_IDNoAnalytics, optional
NEXT_PUBLIC_EMAIL_SIGNIN_REDIRECT_URLYesSet to your production /complete-login URL (see below)

NEXT_PUBLIC_* values are compiled into the browser bundle. Firebase web credentials are designed to be public — enforce access control with firestore.rules, not by hiding the keys.

Point the sign-in redirect at production

Passwordless email-link sign-in redirects back to the URL in NEXT_PUBLIC_EMAIL_SIGNIN_REDIRECT_URL. In development this is http://localhost:3000/complete-login; in production set it to your deployed origin plus /complete-login:

Vercel → Environment Variables
NEXT_PUBLIC_EMAIL_SIGNIN_REDIRECT_URL=https://shop.example.com/complete-login

Authorize the production domain in Firebase

The redirect domain must be listed in Firebase console → Authentication → Settings → Authorized domains. Add your production hostname (e.g. shop.example.com, and any Vercel preview domain you want sign-in to work on) or the email-link flow is rejected.

Add optional integrations (if used)

Only set these when you actually want the feature — leaving them unset keeps the store quote-only:

FeatureEnv varsEffect when unset
Stripe CheckoutSTRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRETCheckout button never appears
Order recording from webhookFIREBASE_SERVICE_ACCOUNT_JSON (or GOOGLE_APPLICATION_CREDENTIALS/FIREBASE_SERVICE_ACCOUNT path)Webhook acknowledges events but can't write orders
Email notificationsRESEND_API_KEY, RESEND_FROM_EMAIL, QUOTE_NOTIFICATION_EMAILEmails skipped, store works normally
SQL backendNEXT_PUBLIC_COMMERCE_BACKEND + COMMERCE_BACKEND (+ SUPABASE_* or DATABASE_URL)Default Firebase backend runs client-side

Deploy

Trigger the deploy. On success, visit the site, sign in once, and (if you want the admin dashboard) add your user id to the Firestore admins collection.

The Stripe webhook needs a service account

Stripe order recording happens server-side with the Firebase Admin SDK, which needs privileged credentials that the browser never sees. On Vercel the convenient option is FIREBASE_SERVICE_ACCOUNT_JSON — paste the service-account key's JSON contents (not a file path) directly into the environment variable:

Generate a key

Firebase console → Project settings → Service accounts → Generate new private key. This downloads a JSON file. The file is git-ignored — never commit it.

Paste the contents into Vercel

Copy the whole JSON object and paste it as the value of FIREBASE_SERVICE_ACCOUNT_JSON:

Vercel → Environment Variables
FIREBASE_SERVICE_ACCOUNT_JSON={"type":"service_account","project_id":"your-project", ... }

Alternatively, provide a key-file path via GOOGLE_APPLICATION_CREDENTIALS or FIREBASE_SERVICE_ACCOUNT — but a pasted JSON blob is simpler on serverless hosts where there is no persistent filesystem.

Register the webhook endpoint

In the Stripe dashboard, add a webhook pointing at https://your-domain/api/webhooks/stripe and copy its signing secret into STRIPE_WEBHOOK_SECRET.

Without admin credentials the POST /api/webhooks/stripe handler still acknowledges events (so Stripe won't retry-storm) but logs that the order could not be recorded. If paid orders aren't showing up in /admin/orders, check that FIREBASE_SERVICE_ACCOUNT_JSON is set on the deployment.

The same admin credentials also power the npm run seed demo-data script — see Demo data. Note the SQL backends require FIREBASE_SERVICE_ACCOUNT* too, because Firebase Auth stays the identity provider and its ID tokens are verified server-side.

Configure images for your CDN

Product images (primaryImageURL) render through next/image, so every host they can be served from must be listed in images.remotePatterns in next.config.mjs. The default config already allows Firebase Storage and the demo photography host:

next.config.mjs
images: {
  remotePatterns: [
    {
      protocol: "https",
      hostname: "firebasestorage.googleapis.com",
    },
    {
      // Editorial/lifestyle photography used by the storefront sections.
      protocol: "https",
      hostname: "picsum.photos",
    },
  ],
},

If you serve product images from your own CDN or storage bucket, add its hostname here before deploying, or next/image will refuse to optimize those URLs. Uploads to Firebase Storage work with no change — firebasestorage.googleapis.com is already allowed.

next.config.mjs also lists @prisma/client, @prisma/adapter-pg, and pg under experimental.serverComponentsExternalPackages. This keeps the optional SQL-backend packages out of the bundle so a default Firebase build succeeds without them installed — you don't need Prisma or Postgres for a standard deploy.

Continuous integration

A ready-made GitHub Actions workflow lives at docs/ci.example.yml. It runs on pushes to main and on every pull request, and executes lint → typecheck → test → build on Node 20:

docs/ci.example.yml
- name: Lint
  run: npm run lint

- name: Typecheck
  run: npm run typecheck

- name: Test
  run: npm test

- name: Build
  run: npm run build
  # No env needed: src/lib/firebase/config.ts falls back to demo
  # values so the SDK initializes during prerender without credentials.

Activate it by copying it into the workflows directory (it ships outside .github/workflows/ because automated pushes can't create workflow files without extra permissions):

Terminal
mkdir -p .github/workflows && cp docs/ci.example.yml .github/workflows/ci.yml

The build step needs no environment variables — the Firebase config's demo-value fallback lets the SDK initialize during prerender. And because the optional SQL packages are external, the default build never requires @prisma/client, pg, or a DATABASE_URL.

Production readiness

Before going live, don't ship the demo defaults straight to production:

  • Wire a live FX feed. Currency conversion uses demo rates baked into siteConfig.currencies. Prices are stored in the base currency (USD) and converted at display time — replace the demo rates with a real feed for accurate customer-facing prices.
  • Deploy and verify firestore.rules. The shipped rules enforce all access control (admin gating, append-only audit log, public catalog reads). Deploy them with firebase deploy --only firestore:rules and confirm they're active — the UI useIsAdmin check is convenience only. If you use image uploads, deploy storage.rules too (firebase deploy --only storage).
  • Use a real Resend sender. onboarding@resend.dev is for testing only. Set RESEND_FROM_EMAIL to a domain you've verified in Resend before enabling email notifications.
  • Review the legal pages. The privacy/terms/cookie pages under src/app/ are placeholders — review them with counsel.

On this page