The Firebase Emulator Suite lets you run Firestore, Auth, Functions, Storage, and Realtime Database locally so you can develop and test without touching production data or incurring costs. Install the Firebase CLI, run firebase emulators:start, connect your app code with the connectEmulator helpers, and use the Emulator UI at localhost:4000 to inspect data and manage users.
Running Firebase Services Locally with the Emulator Suite
The Firebase Emulator Suite provides local versions of Firestore, Auth, Cloud Functions, Storage, Realtime Database, Hosting, and Pub/Sub. You develop against these local services instead of production, which means zero billing impact and the ability to test security rules, triggers, and auth flows in isolation. This tutorial covers setup, connection, the Emulator UI dashboard, and persisting data across restarts.
Prerequisites
- Firebase CLI installed (npm install -g firebase-tools) and logged in (firebase login)
- A Firebase project initialized in your directory (firebase init)
- Java JDK 11+ installed (required by the Firestore and Realtime Database emulators)
- Node.js 18+ installed
Step-by-step guide
Initialize emulators in your project
Initialize emulators in your project
Run the Firebase init command and select the emulators you want. This creates or updates firebase.json with emulator port configuration. Select at least Firestore, Auth, and Functions for a typical web app. Each emulator runs on a default port that you can customize.
1firebase init emulators23# Select: Firestore, Auth, Functions, Storage4# Accept default ports or customize:5# Firestore: 80806# Auth: 90997# Functions: 50018# Storage: 91999# Emulator UI: 4000Expected result: firebase.json includes an emulators block with ports for each selected service.
Start the emulators
Start the emulators
Run the start command to launch all configured emulators. The terminal will display each emulator's URL and port. The Emulator UI launches at localhost:4000 by default and provides a visual dashboard for browsing Firestore documents, managing Auth users, and viewing Function logs.
1firebase emulators:startExpected result: All selected emulators are running and the Emulator UI is accessible at http://localhost:4000.
Connect your app to the emulators
Connect your app to the emulators
In your app's Firebase initialization code, call the connect functions for each service you want to route to the local emulator. Wrap these calls in a conditional check so they only run during development. Each service has its own connect function: connectFirestoreEmulator, connectAuthEmulator, connectFunctionsEmulator, and connectStorageEmulator.
1// src/lib/firebase.ts2import { initializeApp } from 'firebase/app'3import { getFirestore, connectFirestoreEmulator } from 'firebase/firestore'4import { getAuth, connectAuthEmulator } from 'firebase/auth'5import { getFunctions, connectFunctionsEmulator } from 'firebase/functions'6import { getStorage, connectStorageEmulator } from 'firebase/storage'78const app = initializeApp({ /* your config */ })910export const db = getFirestore(app)11export const auth = getAuth(app)12export const functions = getFunctions(app)13export const storage = getStorage(app)1415if (import.meta.env.DEV) {16 connectFirestoreEmulator(db, '127.0.0.1', 8080)17 connectAuthEmulator(auth, 'http://127.0.0.1:9099')18 connectFunctionsEmulator(functions, '127.0.0.1', 5001)19 connectStorageEmulator(storage, '127.0.0.1', 9199)20}Expected result: All Firebase SDK calls in your app now route to the local emulators when running in development mode.
Create test data and users in the Emulator UI
Create test data and users in the Emulator UI
Open http://localhost:4000 in your browser. The Firestore tab lets you create collections and documents manually. The Auth tab lets you add test users with email/password, set custom claims, and generate auth tokens. The Functions tab shows live logs from your Cloud Functions. Use these tools to set up test scenarios without writing seed scripts.
Expected result: Test users and documents appear in the Emulator UI and are accessible from your running app.
Export and import emulator data
Export and import emulator data
By default, emulators clear all data when stopped. Use the export flag to save state to a directory, and the import flag to restore it on the next start. This lets you build up a test dataset once and reuse it across development sessions.
1# Export data when stopping2firebase emulators:start --export-on-exit=./emulator-data34# Import data on next start5firebase emulators:start --import=./emulator-dataExpected result: Emulator data persists between sessions when using the import and export flags.
Test security rules against the emulators
Test security rules against the emulators
The emulators enforce your Firestore and Storage security rules, making them ideal for testing access control. Install the @firebase/rules-unit-testing package and write test cases that use assertSucceeds and assertFails to verify your rules allow and deny the correct operations.
1npm install --save-dev @firebase/rules-unit-testing23// tests/firestore.rules.test.ts4import { assertSucceeds, assertFails, initializeTestEnvironment } from '@firebase/rules-unit-testing'5import { readFileSync } from 'fs'67const testEnv = await initializeTestEnvironment({8 projectId: 'test-project',9 firestore: {10 rules: readFileSync('firestore.rules', 'utf8'),11 host: '127.0.0.1',12 port: 808013 }14})1516const authedContext = testEnv.authenticatedContext('user-123')17const unauthedContext = testEnv.unauthenticatedContext()1819await assertSucceeds(20 authedContext.firestore().collection('posts').doc('test').get()21)2223await assertFails(24 unauthedContext.firestore().collection('posts').doc('test').get()25)Expected result: Tests pass when rules correctly allow authenticated reads and deny unauthenticated reads.
Complete working example
1// src/lib/firebase.ts2import { initializeApp } from 'firebase/app'3import { getFirestore, connectFirestoreEmulator } from 'firebase/firestore'4import { getAuth, connectAuthEmulator } from 'firebase/auth'5import { getFunctions, connectFunctionsEmulator } from 'firebase/functions'6import { getStorage, connectStorageEmulator } from 'firebase/storage'78const firebaseConfig = {9 apiKey: import.meta.env.VITE_FIREBASE_API_KEY,10 authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,11 projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,12 storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,13 messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,14 appId: import.meta.env.VITE_FIREBASE_APP_ID15}1617const app = initializeApp(firebaseConfig)1819export const db = getFirestore(app)20export const auth = getAuth(app)21export const functions = getFunctions(app)22export const storage = getStorage(app)2324// Connect to emulators in development25if (import.meta.env.DEV) {26 connectFirestoreEmulator(db, '127.0.0.1', 8080)27 connectAuthEmulator(auth, 'http://127.0.0.1:9099')28 connectFunctionsEmulator(functions, '127.0.0.1', 5001)29 connectStorageEmulator(storage, '127.0.0.1', 9199)30 console.log('Connected to Firebase Emulators')31}Common mistakes when using Firebase Emulator Suite
Why it's a problem: Calling connectEmulator functions after making a Firestore or Auth request, which throws 'Firestore has already been started'
How to avoid: Call all connectEmulator functions immediately after initializing the service, before any reads, writes, or auth calls. Place them right after getFirestore/getAuth in your initialization file.
Why it's a problem: Leaving emulator connection code active in production, routing all traffic to localhost
How to avoid: Always wrap connectEmulator calls in a DEV check (import.meta.env.DEV for Vite, process.env.NODE_ENV for Node). Double-check your production build does not include emulator connections.
Why it's a problem: Forgetting to install Java, causing the Firestore and Realtime Database emulators to fail to start
How to avoid: The Firestore and RTDB emulators require Java JDK 11+. Install it from adoptium.net or your system package manager and ensure java is available in your PATH.
Best practices
- Use --export-on-exit and --import flags to persist test data between emulator sessions
- Always use a DEV-only conditional to connect to emulators so production builds are never affected
- Use 127.0.0.1 instead of localhost to prevent IPv6 resolution issues on macOS and Linux
- Write security rules tests with @firebase/rules-unit-testing to catch permission bugs before deployment
- Keep a small seed dataset in version control so all team members start with the same test data
- Use the Emulator UI at localhost:4000 to inspect Firestore data and Function logs instead of adding console.log statements
- Run emulators in CI pipelines to validate security rules and Cloud Functions as part of your test suite
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Show me how to set up the Firebase Emulator Suite for a project that uses Firestore, Auth, and Cloud Functions. Include the firebase.json emulator config, the code to connect my app to emulators in development only, and how to export and import emulator data between sessions.
Configure the Firebase Emulator Suite for my project. I need Firestore on port 8080, Auth on 9099, and Functions on 5001. Show me how to connect my web app to these emulators only during development and how to write a basic security rules test using @firebase/rules-unit-testing.
Frequently asked questions
Do emulators require a Firebase project or billing account?
No. Emulators run entirely on your local machine and do not connect to Google Cloud. You do not need a Blaze plan or any billing setup. You still need a firebase project ID in your config, but it can be a dummy value for local-only testing.
Does the Emulator Suite enforce security rules?
Yes. The Firestore and Storage emulators enforce your security rules files. The Auth emulator simulates authentication so your rules can check request.auth. This makes emulators ideal for testing access control before deploying.
Can I use emulators in a CI pipeline?
Yes. Run firebase emulators:exec 'npm test' to start emulators, run your test command, and shut down emulators automatically. This is the recommended approach for CI environments.
Why does my emulator data disappear when I stop the process?
By default, emulators clear all data on shutdown. Use --export-on-exit=./emulator-data to save state automatically, and --import=./emulator-data on the next start to restore it.
Can I run only specific emulators instead of all of them?
Yes. Use firebase emulators:start --only firestore,auth to start specific emulators. This is faster when you only need to test certain services.
How do I access the Emulator UI?
Open http://localhost:4000 in your browser after running firebase emulators:start. The UI provides tabs for each running emulator where you can browse Firestore data, manage Auth users, view Function logs, and inspect Storage files.
Are there differences between the emulator and production?
Emulators do not enforce billing quotas, rate limits, or all production constraints. Some features like full-text search in Data Connect and certain Cloud Function triggers may behave differently. Always run final validation against a staging Firebase project.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation