# How to Add Firebase to an Existing Web Project

- Tool: Firebase
- Difficulty: Beginner
- Time required: 10-15 min
- Compatibility: Firebase (all plans), firebase 10.x+, any JavaScript/TypeScript framework
- Last updated: March 2026

## TL;DR

To add Firebase to an existing web project, create a Firebase project in the Console, register a web app to get your config object, install the firebase npm package, and call initializeApp() with your config. Store config values in environment variables for security. The modular v9+ SDK uses tree-shakeable imports like getFirestore and getAuth, keeping your bundle size small. Works with React, Next.js, Vue, and any JavaScript framework.

## Adding Firebase to an Existing Web Project with the Modular SDK

This tutorial walks you through connecting Firebase to an existing JavaScript or TypeScript project. You will create a Firebase project, register a web app, install the SDK, and write a reusable initialization module using the modular v9+ syntax. The guide covers environment variable setup for security and shows how to import individual Firebase services like Firestore, Auth, and Storage.

## Before you start

- A Google account to access the Firebase Console
- An existing web project with npm/yarn initialized (package.json exists)
- Node.js 18 or later installed
- A code editor like VS Code or Cursor

## Step-by-step guide

### 1. Create a Firebase project in the Console

Go to console.firebase.google.com and click Add project. Enter a project name, optionally enable Google Analytics, and click Create project. This takes about 30 seconds. Once created, you land on the project dashboard where you can add apps and enable services.

**Expected result:** A new Firebase project is created and visible in your Firebase Console dashboard.

### 2. Register a web app and copy the config

From the project dashboard, click the web icon (</>) to add a web app. Give it a nickname (e.g., your project name). You do not need to enable Firebase Hosting at this step. After registering, Firebase displays your config object containing apiKey, authDomain, projectId, and other values. Copy this entire config object.

```
// Firebase displays this config object after registering your app:
const firebaseConfig = {
  apiKey: "AIzaSy...",
  authDomain: "your-project.firebaseapp.com",
  projectId: "your-project-id",
  storageBucket: "your-project.appspot.com",
  messagingSenderId: "123456789",
  appId: "1:123456789:web:abc123",
};
```

**Expected result:** You have a firebaseConfig object with your project's specific values.

### 3. Install the Firebase SDK

In your project root, install the firebase npm package. This single package includes all Firebase client services: Firestore, Auth, Storage, Analytics, and more. You import only what you need thanks to the modular v9+ architecture.

```
npm install firebase

# Or with yarn:
yarn add firebase
```

**Expected result:** The firebase package appears in your package.json dependencies.

### 4. Create a Firebase initialization module

Create a file (e.g., src/lib/firebase.ts) that initializes Firebase and exports the service instances you need. Use the modular v9+ import syntax which enables tree-shaking — your bundler only includes the Firebase code you actually use. Initialize each service once and export it for use throughout your app.

```
// src/lib/firebase.ts
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
import { getAuth } from "firebase/auth";
import { getStorage } from "firebase/storage";

const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};

const app = initializeApp(firebaseConfig);

export const db = getFirestore(app);
export const auth = getAuth(app);
export const storage = getStorage(app);
```

**Expected result:** A firebase.ts module exports initialized db, auth, and storage instances ready for import anywhere in your app.

### 5. Set up environment variables

Store your Firebase config values in environment variables instead of hardcoding them. Create a .env.local file in your project root for local development. For production, set the same variables in your hosting platform's environment settings (Vercel, Netlify, etc.).

```
# .env.local (Next.js)
NEXT_PUBLIC_FIREBASE_API_KEY=AIzaSy...
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=your-project-id
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your-project.appspot.com
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=123456789
NEXT_PUBLIC_FIREBASE_APP_ID=1:123456789:web:abc123

# For Vite projects, use VITE_ prefix:
# VITE_FIREBASE_API_KEY=AIzaSy...
```

**Expected result:** Environment variables are loaded at build time and your Firebase config uses them instead of hardcoded values.

### 6. Use Firebase services in your app

Import the exported service instances from your firebase.ts module and use them throughout your application. Each Firebase service has its own modular functions that you import separately. This keeps imports clean and bundle sizes small.

```
// Example: Read a document from Firestore
import { db } from "@/lib/firebase";
import { doc, getDoc } from "firebase/firestore";

async function getUser(userId: string) {
  const userDoc = await getDoc(doc(db, "users", userId));
  if (userDoc.exists()) {
    return { id: userDoc.id, ...userDoc.data() };
  }
  return null;
}

// Example: Check auth state
import { auth } from "@/lib/firebase";
import { onAuthStateChanged } from "firebase/auth";

onAuthStateChanged(auth, (user) => {
  if (user) {
    console.log("Signed in:", user.uid);
  } else {
    console.log("Not signed in");
  }
});
```

**Expected result:** Your app can read from Firestore and check auth state using the initialized Firebase services.

## Complete code example

File: `src/lib/firebase.ts`

```typescript
// Firebase initialization module
// Import this file anywhere you need Firebase services
import { initializeApp, getApps } from "firebase/app";
import { getFirestore } from "firebase/firestore";
import { getAuth } from "firebase/auth";
import { getStorage } from "firebase/storage";
import { getAnalytics, isSupported } from "firebase/analytics";

const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};

// Prevent re-initialization in development (hot reload)
const app =
  getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0];

// Core services
export const db = getFirestore(app);
export const auth = getAuth(app);
export const storage = getStorage(app);

// Analytics — only in browser, not during SSR
export const analytics = async () => {
  if (typeof window !== "undefined" && (await isSupported())) {
    return getAnalytics(app);
  }
  return null;
};

export default app;
```

## Common mistakes

- **Calling initializeApp() multiple times due to hot module replacement in development** — undefined Fix: Check getApps().length before initializing: const app = getApps().length === 0 ? initializeApp(config) : getApps()[0]. This prevents the 'Firebase App already exists' error.
- **Using the compat (v8) import syntax instead of modular v9+ imports** — undefined Fix: Import from 'firebase/firestore' not 'firebase/compat/firestore'. The compat layer is larger and does not support tree-shaking, resulting in much bigger bundle sizes.
- **Hardcoding Firebase config values directly in source code committed to public repositories** — undefined Fix: Use environment variables (.env.local) for config values and add .env.local to .gitignore. While the apiKey is safe to expose, keeping config in env vars is a best practice for managing multiple environments.
- **Trying to use getAnalytics() during server-side rendering, causing 'window is not defined' errors** — undefined Fix: Guard Analytics initialization with typeof window !== 'undefined' and await isSupported() before calling getAnalytics(). Analytics only works in the browser.

## Best practices

- Use the modular v9+ import syntax (import { getFirestore } from 'firebase/firestore') for tree-shaking and smaller bundles
- Create a single firebase.ts initialization module and import services from it throughout your app
- Store all Firebase config values in environment variables, even though the apiKey is safe to expose publicly
- Guard against re-initialization with getApps().length check to handle hot module replacement in development
- Initialize Analytics only in the browser with isSupported() to avoid SSR errors in Next.js or Nuxt
- Enable only the Firebase services you actually need to keep dependencies minimal

## Frequently asked questions

### Is the Firebase apiKey safe to expose in client-side code?

Yes. The apiKey only identifies your Firebase project. It does not grant access to data. Access control is enforced by Firestore Security Rules, Auth rules, and Storage rules. Treat it like a project ID, not a password.

### Can I use Firebase with both React and Next.js?

Yes. The Firebase SDK works with any JavaScript framework. The only difference is the environment variable prefix: NEXT_PUBLIC_ for Next.js, VITE_ for Vite/Lovable, and REACT_APP_ for Create React App.

### Do I need the Blaze plan to use Firebase in my project?

Not for basic features. Firestore, Auth (email/social), Hosting, and Analytics work on the free Spark plan. You need Blaze for Cloud Functions, phone authentication, and multiple Realtime Database instances.

### How do I avoid the 'Firebase App already exists' error?

Check if Firebase is already initialized before calling initializeApp: const app = getApps().length === 0 ? initializeApp(config) : getApps()[0]. This handles hot module replacement in development.

### What is the difference between the modular v9+ SDK and the compat SDK?

The modular SDK uses individual function imports (getFirestore, doc, getDoc) that enable tree-shaking, reducing bundle size by up to 80%. The compat SDK uses the older firebase.firestore() syntax and includes all code regardless of what you use.

### Can I add Firebase to a project that already uses Supabase?

Yes. Firebase and Supabase can coexist in the same project. A common pattern is using Firebase for Auth and Analytics while using Supabase for the database, or vice versa.

---

Source: https://www.rapidevelopers.com/firebase-tutorial/how-to-add-firebase-to-an-existing-project
© RapidDev — https://www.rapidevelopers.com/firebase-tutorial/how-to-add-firebase-to-an-existing-project
