Web App Config
Firebase config: the firebaseConfig object, environment switches, Remote Config, and the public-vs-private distinction.
Firebase — config
EXAMPLE
// ===== The firebaseConfig object =====
// You get this from the console (Project Settings -> Your apps).
const firebaseConfig = {
apiKey: 'AIzaSy...',
authDomain: 'my-project.firebaseapp.com',
projectId: 'my-project',
storageBucket: 'my-project.appspot.com',
messagingSenderId: '1234567890',
appId: '1:1234567890:web:abc',
};
// IMPORTANT: this is NOT a secret.
// It identifies your project to Firebase.
// Security lives in your Security Rules + App Check, not in hiding apiKey.
// ===== Environment switching =====
// Use env vars at build time:
const config = {
dev: { projectId: 'my-project-dev', ... },
prod: { projectId: 'my-project', ... },
};
const cfg = config[process.env.NODE_ENV ?? 'dev'];
// Or per-bundle .env files:
// .env.development -> VITE_FB_PROJECT_ID=my-project-dev
// .env.production -> VITE_FB_PROJECT_ID=my-project
// In code:
const cfg = {
apiKey: import.meta.env.VITE_FB_API_KEY,
projectId: import.meta.env.VITE_FB_PROJECT_ID,
...
};
// ===== Initialise once =====
import { initializeApp, getApps } from 'firebase/app';
const app = getApps()[0] ?? initializeApp(cfg);
// Hot-reload friendly: getApps() avoids re-init errors.
// ===== Remote Config (server-driven config) =====
import { getRemoteConfig, fetchAndActivate, getValue } from 'firebase/remote-config';
const rc = getRemoteConfig(app);
rc.settings.minimumFetchIntervalMillis = 3600 * 1000;
rc.defaultConfig = { showBanner: false, primaryColor: '#2563eb' };
await fetchAndActivate(rc);
const show = getValue(rc, 'showBanner').asBoolean();
// Use cases:
// - Feature flags
// - A/B test variants
// - UI copy / colours
// - Per-tier rate limits
// ===== App Check =====
import { initializeAppCheck, ReCaptchaV3Provider } from 'firebase/app-check';
initializeAppCheck(app, {
provider: new ReCaptchaV3Provider('reCAPTCHA-site-key'),
isTokenAutoRefreshEnabled: true,
});
// App Check blocks requests from outside your origin / app.
// ===== Functions config (legacy) =====
// firebase functions:config:set stripe.key="sk_..."
// Then in code:
// const key = functions.config().stripe.key;
// Migrating: use environment variables and .env files for new code.
// ===== Local vs cloud =====
import { connectFirestoreEmulator } from 'firebase/firestore';
import { connectAuthEmulator } from 'firebase/auth';
if (location.hostname === 'localhost' && import.meta.env.VITE_USE_EMU) {
connectAuthEmulator(auth, 'http://localhost:9099');
connectFirestoreEmulator(db, 'localhost', 8080);
}
// ===== Patterns to internalise =====
// - firebaseConfig is public; do not hide it as a secret
// - Per-environment configs via env vars at build time
// - Remote Config for runtime feature flags / experiments
// - App Check + locked Security Rules > guarding the apiKey
// ===== Pitfalls =====
// - Treating firebaseConfig as a secret (it is not)
// - Mixing dev + prod projects via the same app
// - Storing real secrets (Stripe keys, JWT secrets) in client config
// - Skipping App Check on a public product -> bot traffic + cost
Why it matters
firebaseConfig is public; security lives in rules + App Check, not in hiding the apiKey. Use env vars to switch between dev / prod projects, Remote Config for runtime flags, App Check to keep bots out. Once these are reflex, the config side of Firebase is solved.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// firebaseConfig.js
export const firebaseConfig = {
apiKey: 'AIza…',
authDomain: 'myapp.firebaseapp.com',
projectId: 'myapp',
storageBucket: 'myapp.appspot.com',
appId: '1:…'
};
Try it Yourself »
Discussion
Loading…