· 4 min read
How to Inspect What a Web App Stored Locally
Heshan Fernando
Co-founder & COO
An offline-capable web app is showing a document that shouldn’t exist any more. Or it’s not showing one that definitely should. Either way the answer is in local storage — specifically in IndexedDB, which is where anything larger than a few kilobytes ends up.
The catch is that IndexedDB is invisible. There’s no file you can open, no table you can query from a console you already have open. You need something that speaks the API.
What IndexedDB actually is
IndexedDB is a transactional, key-value database built into the browser. Each origin gets its own set of databases; each database contains object stores; each store holds records keyed by a primary key, with optional indexes over other fields.
Two concepts do most of the work.
The key path is how a record’s primary key is derived — usually a field like id. It’s what get() looks up against, and it’s unique within the store.
An index is a secondary lookup structure over another field. Query a field with an index and it’s fast; query one without and you’re iterating every record in the store, which is why an app that felt instant with fifty records crawls at fifty thousand.
Storage is also per origin and, crucially, not guaranteed to persist. Browsers evict data under pressure unless the app has explicitly requested persistent storage.
Why people get stuck here
- You can’t see it. Nothing about IndexedDB is visible without a tool that queries it.
- Version confusion. Databases carry a version number, and an upgrade handler that didn’t run leaves the schema behind what the code expects.
- Same-origin restriction. You can only inspect the origin you’re on — which is correct, and occasionally inconvenient.
- Devtools awkwardness. On a phone, a kiosk, or a locked-down machine, opening a storage panel isn’t always practical.
What good inspection tells you
Record counts per store
The first useful number. A store you expected to hold hundreds holding zero tells you the write path is broken, not the read path.
Which fields are indexed
If a slow query filters on a field with no index, that’s the answer. You can see it in the store definition rather than inferring it from timings.
The schema version actually in use
Comparing the version the browser holds against the version the code expects explains a whole category of “works on my machine” bugs.
| Symptom | Likely Cause | Where To Look |
|---|---|---|
| Query is slow | Filtering on an unindexed field | Index list on the store |
| Record missing | Write failed or was evicted | Record count per store |
| Upgrade handler not running | Version already matches | Database version number |
Common mistakes to avoid
- Treating IndexedDB as durable storage. Clearing site data removes it, and browsers evict under pressure. It’s a cache, unless you’ve requested persistence and got it.
- Storing large blobs without a cleanup path, then hitting a quota error months later.
- Assuming a failed transaction rolled back partially — it doesn’t; transactions are all or nothing, which is usually what you want.
- Reading the whole store into memory to find one record, when an index would do it.
- Debugging storage on desktop and shipping to mobile, where quotas are tighter and eviction is more aggressive.
How to do it with IndexedDB Inspector
The IndexedDB Inspector lists databases, stores, indexes and record counts for the current origin.
- Pick a database detected on the origin, or type the name your app uses.
- Choose a store and whether to show values as well as keys.
- Read the record counts first — they answer most questions immediately.
- Check the index list against the queries your code makes.
Other browser-side debugging utilities are in the tools directory.
Frequently asked questions
Why can I only see this site’s databases?
IndexedDB is partitioned per origin by design — one site can never read another’s storage, and neither can any page. To inspect a different app’s data, open the tool on that origin or use your browser’s devtools there.
Does clearing site data delete IndexedDB?
Yes. It’s cleared alongside cookies and local storage in most “clear site data” flows, which is exactly why it should never hold the only copy of anything a user cares about.
What’s the difference between a key path and an index?
The key path derives the primary key — one per store, unique. An index is an additional lookup over another field, and you can have several. Queries on indexed fields are fast; everything else is a scan.
Final thought
Check the record counts before anything else. Most IndexedDB bugs are a write that never happened or an eviction that did, and both show up in a single number.