TL;DR
I hit FirebaseError: Expected first argument to collection() to be a CollectionReference, a DocumentReference or FirebaseFirestore while writing a Next.js page that queried Firestore. 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.
-
Symptom:
FirebaseError: Expected first argument to collection() to be a CollectionReference, a DocumentReference or FirebaseFirestore -
Root cause: The v9 modular SDK requires the Firestore instance (
db) as the first argument tocollection(); legacy v8 code or missing initialization passes an invalid reference. -
Fix: Ensure you call
getFirestore(app)once, store the result, and always pass it as the first argument. -
Verification: After the fix,
collection(db, 'cities')returns aCollectionReferencewithout error, and you can chain.get()oronSnapshot()as expected.
What you'll see
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:
- A Firestore instance (the thing returned by
getFirestore()) - 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.
I first ran into this after upgrading 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:
// ❌ 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:
// ✅ 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.
- Create
lib/firebase.jsand export thedbinstance. - In your page or component, import
dband pass it to every Firestore function. - Replace any old-style
collection('path')calls withcollection(db, 'path').
A typical Next.js setup file:
// 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:
// 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
// ❌ 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:
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:
// ❌ 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:
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:
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:
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:
// 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.
Related
- Firestore PERMISSION_DENIED: Every Real Cause and Fix — once the collection argument is fixed, the next wall you’ll hit is often a permissions error; this guide breaks down every variant.
- Supabase vs Firebase Authentication: Which is Better — if you’re re‑evaluating Firebase after hitting SDK quirks, this comparison helps decide whether Supabase’s Auth flow fits your Next.js stack.
- Cloud Firestore vs Realtime Database in 2026 — a deeper look at why the modular SDK exists and how it improves performance, choice of database matters when you’re picking the right tool.
Originally published at https://www.iloveblogs.blog
Top comments (0)