The short answer
Chrome’s DevTools Application panel will show you IndexedDB records but it will not let you change them. Keys and values are read-only there. Chrome’s own documentation says so, and points you at the Console instead.
So there are three real options, in increasing order of how often you need to do this:
- Run a one-off
put()in the Console. - Save a reusable DevTools Snippet, so you stop rewriting the same twenty lines.
- Use a panel that makes the grid itself editable.
All three are below, including the parts that bite: the transaction that closes before your callback runs, and the difference between an inline key and an out-of-line one.
Why the Application panel is read-only
The Application panel is a viewer. It opens a read transaction, advances a cursor, and renders what it finds. It deliberately does not expose a write path, so there is no cell you can type into and no context-menu edit. You can delete a selected record, and you can clear an entire object store, but you cannot change a field.
This is not a bug you can configure around, and it is the reason nearly every search for editing IndexedDB lands on a Console workaround. DevTools does give you one useful lever though: code you run in the Console executes in the inspected page’s context, so it can reach the same databases the page can.
Option 1: edit one value from the Console
IndexedDB’s API is event-based and predates promises, which is why the shortest correct snippet is still this long. Open the Console on the page that owns the data and run:
// Edit a single record by key.
// Replace: DB_NAME, STORE_NAME, RECORD_KEY, and the mutation.
const req = indexedDB.open("DB_NAME");
req.onsuccess = () => {
const db = req.result;
const tx = db.transaction("STORE_NAME", "readwrite");
const store = tx.objectStore("STORE_NAME");
const getReq = store.get("RECORD_KEY");
getReq.onsuccess = () => {
const record = getReq.result;
if (!record) {
console.warn("No record for that key");
return;
}
// --- your change goes here ---
record.status = "delivered";
// -----------------------------
// Inline key (store was created with a keyPath): put(value)
// Out-of-line key: put(value, key)
store.put(record);
};
tx.oncomplete = () => {
console.log("done");
db.close();
};
tx.onerror = () => console.error(tx.error);
};Two things go wrong here more than anything else.
Do not open the database without a version argument if you are unsure it exists. indexedDB.open("name") on a name that does not exist will happily create an empty database rather than fail, and then your get() returns undefined and you spend ten minutes wondering where the data went. Check the exact name in the Application panel first.
Do not await anything between opening the transaction and using it. An IndexedDB transaction auto-commits when the microtask queue drains without a pending request. Wrap the raw API in promises and you will eventually hit TransactionInactiveError on a transaction that looked fine a line earlier. Keep the work inside the event callbacks, as above.
Inline keys versus out-of-line keys
If the store was created with a keyPath, the key lives inside the record and you call put(value). If it was created without one, the key is stored separately and you must call put(value, key). Getting this backwards throws DataError. You can check which you are dealing with:
const tx = db.transaction("STORE_NAME", "readonly");
const store = tx.objectStore("STORE_NAME");
console.log({
keyPath: store.keyPath, // null means out-of-line
autoIncrement: store.autoIncrement,
indexes: [...store.indexNames],
});Option 2: save it as a DevTools Snippet
If you are editing storage more than once, retyping that block is waste. DevTools Snippets are saved scripts that run in the inspected page’s context, and they persist across sessions.
Open DevTools, go to Sources, then the Snippets pane, and create a new snippet. Paste something parameterised:
// Snippet: patch an IndexedDB record.
// Set these four, then Ctrl/Cmd+Enter to run.
const DB = "myapp";
const STORE = "orders";
const KEY = "ord_1042";
const PATCH = { status: "delivered", updatedAt: new Date() };
const open = indexedDB.open(DB);
open.onsuccess = () => {
const db = open.result;
const tx = db.transaction(STORE, "readwrite");
const store = tx.objectStore(STORE);
const get = store.get(KEY);
get.onsuccess = () => {
if (!get.result) return console.warn("missing:", KEY);
const next = { ...get.result, ...PATCH };
store.keyPath === null ? store.put(next, KEY) : store.put(next);
console.log("patched", KEY, next);
};
tx.onerror = () => console.error(tx.error);
tx.oncomplete = () => db.close();
};Run it with Cmd+Enter on macOS or Ctrl+Enter elsewhere. Note that the Application panel does not live-update, so refresh the object store view afterwards or you will be looking at a stale render of data you just changed.
This is the best you can do with built-in tooling, and for many people it is genuinely enough. Where it runs out is bulk work: applying the same change to every record matching a condition means writing a cursor loop, and doing it safely means writing the dry-run version first.
Option 3: make the grid editable
The reason a database client feels different from a viewer is that editing is a normal interaction rather than a scripting task. This is the gap IdxBeaver was built to close: it adds a DevTools panel where the object store renders as a grid you can type into.

Concretely, for the editing case:
- Click a cell, type, press enter. The write is committed through the same
put()path, with the inline versus out-of-line key distinction handled for you. - Every commit lands on an undo stack, so a mistyped value is
Cmd+Zrather than a restore-from-backup problem. - Filter first, then edit or delete the matching set, instead of hand-writing a cursor loop for a bulk change.
- Values that are not plain JSON survive the trip.
Date,BigInt,Map,Set,ArrayBuffer,Bloband circular references round-trip through a versioned wire format rather than being flattened byJSON.stringify.
It is free, MIT-licensed, and makes no network requests. If you want the detailed feature-by-feature version, there is a comparison against the built-in Application panel and one covering the other IndexedDB extensions, including where each of them is the better pick.
Editing many records at once
For completeness, here is the Console version of a conditional bulk update, since this is the point where people usually go looking for a tool. Run the dry pass first and read the output before you let it write anything.
// Bulk patch every record matching a predicate.
// DRY_RUN = true logs what would change and writes nothing.
const DB = "myapp";
const STORE = "orders";
const DRY_RUN = true;
const matches = (r) => r.status === "pending" && r.total > 20000;
const patch = (r) => ({ ...r, status: "review" });
const open = indexedDB.open(DB);
open.onsuccess = () => {
const db = open.result;
const tx = db.transaction(STORE, DRY_RUN ? "readonly" : "readwrite");
const store = tx.objectStore(STORE);
const seen = [];
store.openCursor().onsuccess = (event) => {
const cursor = event.target.result;
if (!cursor) return;
if (matches(cursor.value)) {
const next = patch(cursor.value);
seen.push({ key: cursor.key, before: cursor.value, after: next });
if (!DRY_RUN) cursor.update(next);
}
cursor.continue();
};
tx.oncomplete = () => {
console.log(DRY_RUN ? "would change" : "changed", seen.length);
console.table(seen.map((s) => ({ key: s.key, status: s.after.status })));
db.close();
};
tx.onerror = () => console.error(tx.error);
};cursor.update() is the right call inside a cursor walk, not store.put(). It reuses the cursor’s current key, which keeps the inline versus out-of-line question from coming up at all.
A word on doing this to other people's sites
Everything here works on any origin you can open DevTools against, which includes sites you did not build. Editing your own application’s data while debugging is ordinary work. Editing a third party’s stored state to change what their app does is a different thing, and client-side storage is not a security boundary you should be relying on either, if you are on the other side of this question. Server-side validation is the boundary.
Related reading
- Debugging IndexedDB in Chrome DevTools covers the inspection workflow around this.
- Querying IndexedDB with MongoDB-style filters goes into finding the rows you want to change.
- Exporting IndexedDB data is the safety net: take a snapshot before a bulk edit.