How to Use Cursor Pagination in Firestore for Efficient Data Loading
Use Firestore's
startAfter() or startAt() methods combined with limit() to paginate query results. Fetch the first page with a limit, then use the last document snapshot as a cursor for the next page to continue loading data efficiently.Syntax
Cursor pagination in Firestore uses query cursors like startAfter() or startAt() to mark where the next page of results begins. You combine these with limit() to control how many documents to fetch per page.
collectionRef.limit(n): Limits the number of documents ton.startAfter(documentSnapshot): Starts the query after the given document snapshot.startAt(documentSnapshot): Starts the query at the given document snapshot (inclusive).
javascript
const firstPageQuery = collectionRef.orderBy('field').limit(10); const nextPageQuery = collectionRef.orderBy('field').startAfter(lastDoc).limit(10);
Example
This example shows how to load the first 5 documents from a Firestore collection ordered by a field, then load the next 5 documents using cursor pagination.
javascript
import { getFirestore, collection, query, orderBy, limit, startAfter, getDocs } from 'firebase/firestore'; const db = getFirestore(); const colRef = collection(db, 'items'); async function loadFirstPage() { const firstQuery = query(colRef, orderBy('name'), limit(5)); const firstSnapshot = await getDocs(firstQuery); firstSnapshot.docs.forEach(doc => console.log(doc.id, doc.data())); return firstSnapshot.docs[firstSnapshot.docs.length - 1]; } async function loadNextPage(lastDoc) { const nextQuery = query(colRef, orderBy('name'), startAfter(lastDoc), limit(5)); const nextSnapshot = await getDocs(nextQuery); nextSnapshot.docs.forEach(doc => console.log(doc.id, doc.data())); return nextSnapshot.docs[nextSnapshot.docs.length - 1]; } (async () => { const lastDoc = await loadFirstPage(); if (lastDoc) { await loadNextPage(lastDoc); } })();
Output
Logs first 5 documents ordered by 'name', then logs next 5 documents after the last one from first page.
Common Pitfalls
Common mistakes when using cursor pagination in Firestore include:
- Not ordering the query by the same field used in the cursor, which causes errors.
- Using
startAfter()without a valid document snapshot as cursor. - Not handling the case when there are no more documents to paginate.
- Mixing
startAt()andstartAfter()incorrectly, leading to duplicate or skipped results.
javascript
/* Wrong: Missing orderBy causes error */ const wrongQuery = query(colRef, startAfter(lastDoc), limit(5)); /* Right: Always include orderBy on the same field */ const rightQuery = query(colRef, orderBy('name'), startAfter(lastDoc), limit(5));
Quick Reference
| Method | Purpose | Notes |
|---|---|---|
| limit(n) | Limits results to n documents | Use to set page size |
| orderBy(field) | Sorts documents by field | Required for cursor pagination |
| startAfter(doc) | Starts query after given document | Use last doc snapshot from previous page |
| startAt(doc) | Starts query at given document (inclusive) | Includes the document in results |
Key Takeaways
Always use orderBy with cursor methods for consistent pagination.
Use limit to control how many documents load per page.
Pass the last document snapshot from the previous page to startAfter for the next page.
Handle empty results to know when pagination ends.
Avoid mixing startAt and startAfter incorrectly to prevent duplicates.