IndexedDB Cursors: Iterate Large Datasets Efficiently

An IndexedDB cursor is a pointer for walking through records one by one, avoiding loading a whole dataset into memory. Use it to efficiently process large browser databases.
Why it exists
Loading a large dataset from IndexedDB with getAll() can be slow and consume significant memory, potentially freezing the browser's main thread. This makes it impractical for collections with thousands or millions of records. Cursors were created to provide a memory-efficient, non-blocking way to process records individually.
The mental model
An IndexedDB cursor is like a database pointer that you can move through the records of an object store or index. It doesn't load the whole collection at once. Instead, you open a cursor, get a reference to the first record, process it, and then explicitly tell the cursor to advance to the next one. This one-by-one processing is the key to its efficiency and performance.
How it works
You call openCursor() on an IDBObjectStore or IDBIndex, which returns an IDBRequest object. You then attach an onsuccess event handler to this request. When a record is found, the handler fires, and its event.target.result contains the cursor object. If the cursor is not null, you can access the data via cursor.value. To proceed to the next record, you must call cursor.continue(). When no more records are available, the event.target.result will be null, signaling the end of the iteration.
When to use it
Use cursors when dealing with datasets too large to fit comfortably in memory. This is ideal for tasks like searching for a specific record in a massive store, performing a bulk update on a subset of records, or displaying paginated data without fetching everything upfront. It's the standard, performant way to handle non-trivial amounts of data in IndexedDB.
When not to use it
If you know you're only dealing with a small, predictable number of records, using get() for a single record or getAll() for a small collection can be simpler and require less boilerplate code. Cursors add asynchronous complexity that is unnecessary for small-scale operations where memory usage is not a concern.
One canonical example
To iterate all records in a 'notes' object store, you first open a read-only transaction. Then, get the object store and call openCursor(). In the onsuccess handler for the resulting request, you check if event.target.result (the cursor) exists. If it does, you can process its data with cursor.value. Crucially, you must then call cursor.continue() to trigger the onsuccess handler for the next record. If the cursor is null, you know you have processed all records.
Interview question
In which scenario would using getAll() typically be more appropriate than an IndexedDB cursor?
- a.When the data needs to be filtered based on a complex, multi-field condition.
- b.When the number of records is small and memory usage is not a significant issue.Correct
- c.When the application requires paginated display of a very large dataset.
- d.When performing bulk updates on a subset of records in an object store.
Why? this is the answer
The card states that for small, predictable numbers of records where memory usage is not a concern, getAll() is simpler and requires less boilerplate. Cursors are specifically recommended for large datasets, making option C, which describes a large dataset use case, incorrect for getAll().
Just read this? Test yourself on what you have been reading.
Read the original → developer.mozilla.org
- #indexeddb
- #web apis
- #performance
You just looked this up. Could you explain it out loud?
That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Every open role lists the topics its interview covers, so you can prepare for the real thing rather than guessing.
See open roles