# How to Use Firebase Emulator Suite

- Tool: Firebase
- Difficulty: Beginner
- Time required: 10-15 min
- Compatibility: Firebase CLI 13+, Firebase JS SDK v9+, Node.js 18+
- Last updated: March 2026

## TL;DR

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.

## Before you start

- 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

### 1. 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.

```
firebase init emulators

# Select: Firestore, Auth, Functions, Storage
# Accept default ports or customize:
# Firestore: 8080
# Auth: 9099
# Functions: 5001
# Storage: 9199
# Emulator UI: 4000
```

**Expected result:** firebase.json includes an emulators block with ports for each selected service.

### 2. 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.

```
firebase emulators:start
```

**Expected result:** All selected emulators are running and the Emulator UI is accessible at http://localhost:4000.

### 3. 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.

```
// src/lib/firebase.ts
import { initializeApp } from 'firebase/app'
import { getFirestore, connectFirestoreEmulator } from 'firebase/firestore'
import { getAuth, connectAuthEmulator } from 'firebase/auth'
import { getFunctions, connectFunctionsEmulator } from 'firebase/functions'
import { getStorage, connectStorageEmulator } from 'firebase/storage'

const app = initializeApp({ /* your config */ })

export const db = getFirestore(app)
export const auth = getAuth(app)
export const functions = getFunctions(app)
export const storage = getStorage(app)

if (import.meta.env.DEV) {
  connectFirestoreEmulator(db, '127.0.0.1', 8080)
  connectAuthEmulator(auth, 'http://127.0.0.1:9099')
  connectFunctionsEmulator(functions, '127.0.0.1', 5001)
  connectStorageEmulator(storage, '127.0.0.1', 9199)
}
```

**Expected result:** All Firebase SDK calls in your app now route to the local emulators when running in development mode.

### 4. 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.

### 5. 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.

```
# Export data when stopping
firebase emulators:start --export-on-exit=./emulator-data

# Import data on next start
firebase emulators:start --import=./emulator-data
```

**Expected result:** Emulator data persists between sessions when using the import and export flags.

### 6. 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.

```
npm install --save-dev @firebase/rules-unit-testing

// tests/firestore.rules.test.ts
import { assertSucceeds, assertFails, initializeTestEnvironment } from '@firebase/rules-unit-testing'
import { readFileSync } from 'fs'

const testEnv = await initializeTestEnvironment({
  projectId: 'test-project',
  firestore: {
    rules: readFileSync('firestore.rules', 'utf8'),
    host: '127.0.0.1',
    port: 8080
  }
})

const authedContext = testEnv.authenticatedContext('user-123')
const unauthedContext = testEnv.unauthenticatedContext()

await assertSucceeds(
  authedContext.firestore().collection('posts').doc('test').get()
)

await assertFails(
  unauthedContext.firestore().collection('posts').doc('test').get()
)
```

**Expected result:** Tests pass when rules correctly allow authenticated reads and deny unauthenticated reads.

## Complete code example

File: `src/lib/firebase.ts`

```typescript
// src/lib/firebase.ts
import { initializeApp } from 'firebase/app'
import { getFirestore, connectFirestoreEmulator } from 'firebase/firestore'
import { getAuth, connectAuthEmulator } from 'firebase/auth'
import { getFunctions, connectFunctionsEmulator } from 'firebase/functions'
import { getStorage, connectStorageEmulator } from 'firebase/storage'

const firebaseConfig = {
  apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
  authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
  projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
  storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
  appId: import.meta.env.VITE_FIREBASE_APP_ID
}

const app = initializeApp(firebaseConfig)

export const db = getFirestore(app)
export const auth = getAuth(app)
export const functions = getFunctions(app)
export const storage = getStorage(app)

// Connect to emulators in development
if (import.meta.env.DEV) {
  connectFirestoreEmulator(db, '127.0.0.1', 8080)
  connectAuthEmulator(auth, 'http://127.0.0.1:9099')
  connectFunctionsEmulator(functions, '127.0.0.1', 5001)
  connectStorageEmulator(storage, '127.0.0.1', 9199)
  console.log('Connected to Firebase Emulators')
}
```

## Common mistakes

- **Calling connectEmulator functions after making a Firestore or Auth request, which throws 'Firestore has already been started'** — undefined Fix: 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.
- **Leaving emulator connection code active in production, routing all traffic to localhost** — undefined Fix: 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.
- **Forgetting to install Java, causing the Firestore and Realtime Database emulators to fail to start** — undefined Fix: 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

## 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.

---

Source: https://www.rapidevelopers.com/firebase-tutorial/how-to-use-firebase-emulator-suite
© RapidDev — https://www.rapidevelopers.com/firebase-tutorial/how-to-use-firebase-emulator-suite
