FirebaseError: Expected first argument to collection() — Fix
Next.js

FirebaseError: Expected first argument to collection() — Fix

When Firestore throws 'Expected first argument to collection()', it's almost always a missing Firestore instance. Learn the exact fix for Next.js and v9 modular SDK.

7 min read
FirebaseError: Expected first argument to collection() — Fix

TL;DR#

FirebaseError: Expected first argument to collection() to be a CollectionReference, a DocumentReference or FirebaseFirestore is what Firestore throws from a Next.js page the moment collection() is called without a database instance. It is the symptom described in the original Stack Overflow question (35 upvotes, 54,000+ views). The fix is passing db as the first argument to collection(), where db is the return value of getFirestore() from firebase/firestore.

If that doesn't work, scroll to verify the fix — there are two common variants this guide also covers.

What you'll see#

plaintext
FirebaseError: Expected first argument to collection() to be a CollectionReference, a DocumentReference or FirebaseFirestore
    at collection (index.mjs:…)
    at …

It happens when your Next.js component or API route tries to call collection() (or any function that depends on it, like getDocs, onSnapshot, etc.) after migrating from the namespaced (v8) Firebase API to the modular (v9) one. The behavior is the same across development (next dev), production builds, and even in Cloud Functions if you've switched to the new tree-shakeable API.

The error, decoded#

The error message says the first argument you passed to collection() is not a Firestore instance, a CollectionReference, or a DocumentReference. In the v9 modular SDK, collection() expects two arguments:

  1. A Firestore instance (the thing returned by getFirestore())
  2. A path string (or a reference chain)

If you omit the first argument — or pass something else, like the module default export — you'll see this error.

The usual trigger, and the one in the Stack Overflow thread, is an upgrade from the old firebase/firestore namespace where you could just write db.collection('cities'). The new tree-shakeable API separates functions from the instance, so a direct call like collection('cities') is ambiguous: the SDK doesn't know which database you're targeting.

Why Firestore v9 doesn't auto‑resolve the database#

Under the hood, the v9 SDK decouples functions from a single, global Firestore object. In v8, firebase.firestore() returned a Firestore instance and all calls like collection() were methods on that instance. In v9, collection() is a standalone function imported from firebase/firestore. To be tree-shakeable, it cannot rely on an implicit database — you must hand it the database explicitly.

The code that triggers the error typically looks like this:

js
// ❌ Broken: missing db argument
import { collection, getDocs } from 'firebase/firestore';
 
const citiesCol = collection('cities');
const snapshot = await getDocs(citiesCol);

Here, collection('cities') calls the function with a single string argument. Firebase expects a Firestore instance as the first argument, not a string, so it throws the error.

The fix: always pass the Firestore instance as the first argument#

The corrected version is:

js
// ✅ Fixed
import { collection, getDocs, getFirestore } from 'firebase/firestore';
import { initializeApp } from 'firebase/app';
 
// Initialize Firebase
const app = initializeApp(yourConfig);
const db = getFirestore(app);
 
// Now collection() knows which Firestore instance to use
const citiesCol = collection(db, 'cities');
const snapshot = await getDocs(citiesCol);

That single change — adding db as the first argument — resolves the error because the function now receives a valid FirebaseFirestore object.

Step by step in a Next‑js project#

If you're inside a Next.js app, the cleanest way to avoid this error is to centralize your Firebase initialization.

  1. Create lib/firebase.js and export the db instance.
  2. In your page or component, import db and pass it to every Firestore function.
  3. Replace any old-style collection('path') calls with collection(db, 'path').

A typical Next.js setup file:

js
// lib/firebase.js
import { initializeApp, getApps, getApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';
 
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,
  // ... other config
};
 
const app = !getApps().length ? initializeApp(firebaseConfig) : getApp();
const db = getFirestore(app);
 
export { db };

Then in a page:

js
// pages/index.js
import { collection, getDocs } from 'firebase/firestore';
import { db } from '../lib/firebase';
 
export async function getServerSideProps() {
  const citiesCol = collection(db, 'cities');
  const snapshot = await getDocs(citiesCol);
  const cities = snapshot.docs.map(doc => doc.data());
 
  return { props: { cities } };
}

Two patterns that still trip you up#

Even after adding the db argument, developers sometimes hit the same error because of a subtle misconfiguration. Here are the two most common variants I see.

Pattern 1 — Passing getFirestore instead of calling it#

js
// ❌ Passing the function reference, not an instance
import { collection, getFirestore } from 'firebase/firestore';
const col = collection(getFirestore, 'cities');

The error occurs because getFirestore is a function, not a Firestore instance. The fix is to invoke it:

js
const db = getFirestore(app);
const col = collection(db, 'cities');

Pattern 2 — Using the admin SDK incorrectly in Next.js API routes#

If you use firebase-admin for server‑side Firestore access (e.g., inside API routes), the admin.firestore() call returns a Firestore instance that behaves differently. A similarly‑named error can appear if you accidentally mix the client SDK’s collection function with the admin SDK’s namespace. For example:

js
// ❌ Mixing admin and client functions
const { collection } = require('firebase/firestore');
const admin = require('firebase-admin');
const db = admin.firestore();
const col = collection(db, 'cities'); // still works, but be careful with environment

It works if db is a plain object that matches the expected interface, but on some setups the admin SDK’s Firestore object doesn't share the same prototype, causing a cryptic type‑error variation. The safer route is to use the admin SDK’s own method:

js
const col = db.collection('cities');

However, for client‑side Next.js pages, stick to the modular client SDK as described in the fix above.

Verify the fix#

After applying the change, run your dev server:

bash
npm run dev

Visit the page that queries Firestore. You should see the data rendered without the error. If you want a minimal sanity check, add a console.log to confirm the returned reference type:

js
import { collection, getFirestore } from 'firebase/firestore';
import { app } from '../lib/firebase';
 
const db = getFirestore(app);
const col = collection(db, 'test');
console.log(col.type); // -> 'collection'

The type property prints 'collection', proving that you now have a valid CollectionReference.

If you are still seeing the error, double‑check that your db export is indeed the result of getFirestore(app). A common oversight in Next.js is that lib/firebase.js might export a promise if the Firebase init is lazy‑loaded. Use a synchronous pattern or a module‑level init guard as shown above.

Keep it from coming back#

Add TypeScript types to catch the mistake at compile time. If you define your db export with the correct type, passing a wrong argument will produce a diagnostic:

ts
// lib/firebase.ts
import { getFirestore, Firestore } from 'firebase/firestore';
import { initializeApp, getApps, getApp } from 'firebase/app';
 
let db: Firestore;
 
const app = !getApps().length ? initializeApp(config) : getApp();
db = getFirestore(app);
 
export { db };

Now any call to collection(db, '...') will be type‑checked. If you accidentally write collection(getFirestore, '...'), TypeScript will complain that a function is not assignable to Firestore.

For an additional layer, you can wrap the Firestore calls in a custom hook or utility that always expects db as a parameter, avoiding ad‑hoc calls in components.


FAQ#

1. Can I still use the old syntax with firebase/firestore/compat?#

Yes, Firebase provides a compat library that mimics v8 behavior. If you stick to the compat layer you can continue writing db.collection('cities'). However, you lose the tree‑shaking benefits and the compat layer is not recommended for new projects. The modular API is the forward path, and understanding how collection() expects an instance avoids almost all errors you'll hit in Next.js and modern frontend frameworks. I cover this trade‑off in Cloud Firestore vs Realtime Database in 2026, where the modular improvement is particularly noticeable for bundle size.

2. Does this error also happen if I use doc() instead of collection()?#

Yes, the same principle applies. doc() expects a DocumentReference, a string path starting from the Firestore root, or a Firestore instance followed by a path. In v9, doc(db, 'cities/LA') works, but doc('cities/LA') fails with a similar message. Always pass the database instance as the first argument.

3. What if I’m using Firebase’s React Native SDK?#

The error is identical. React Native uses the same modular Firestore package. Initialize Firebase in your app’s entry point, call getFirestore(app), and pass the returned db to all Firestore functions. If you’re migrating from a React Native project that relied on an older version, the fix is exactly the same. The only difference is that you might need to configure persistence first, but the argument requirement remains unchanged.


Frequently Asked Questions

|

Have more questions? Contact us

Written by

Mahdi Br
Mahdi Br

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote

One email a month — no fluff

RLS gotchas, Next.js cache debugging, and the one Supabase setting that bit me last month.