पाठ 11 / 25
Pagination With Cursors
startAfter, not offsets.
Cursor-based pages
Paginate by ordering on a field and using a query cursor: keep the last document snapshot of the current page and pass it to startAfter for the next page. Cursors are stable when new data arrives and only read the documents you return. Avoid offset-style approaches, because skipped documents are still read and billed in Firestore. Order on a field that is unique, or add a tiebreaker such as the document id, so documents with equal values are not skipped or repeated. startAt, endAt and endBefore are also available, plus limitToLast for paging backwards.
Load more
TypeScript.
import {
collection, query, orderBy, limit, startAfter, getDocs,
QueryDocumentSnapshot, DocumentData,
} from "firebase/firestore";
const PAGE = 20;
let last: QueryDocumentSnapshot<DocumentData> | undefined;
export async function loadMore() {
const posts = collection(db, "posts");
const q = last
? query(posts, orderBy("createdAt", "desc"), startAfter(last), limit(PAGE))
: query(posts, orderBy("createdAt", "desc"), limit(PAGE));
const snap = await getDocs(q);
last = snap.docs[snap.docs.length - 1] ?? last;
return { items: snap.docs.map((d) => ({ id: d.id, ...d.data() })), done: snap.size < PAGE };
}A bookmark, not a page number
You remember the last line you read and continue from there, instead of counting from the start of the book every time.
त्वरित जाँच: Why prefer startAfter over skipping N documents?
- Cursors bypass Security Rules
- startAfter works without any ordering
- Offsets are not allowed in JavaScript
- Skipped documents are still read and billed, while cursors read only the page
Answer
Skipped documents are still read and billed, while cursors read only the page — Cursors are cheaper and stable.