←All posts

Chrome 156 Moves IndexedDB to SQLite: What Changes for Your Offline-First App

Chromium is replacing the LevelDB-based IndexedDB engine with SQLite, and Chrome 156 lists it in the release notes. The API is unchanged, but a transaction-scheduling edge case is not, so it is worth auditing your storage layer now.

Chrome 156 Moves IndexedDB to SQLite: What Changes for Your Offline-First App

Every offline-first app, PWA, and local-first sync engine eventually rests on IndexedDB. Most teams never look under it. That is about to change, at least for a few minutes of reading: Chromium is replacing the engine behind IndexedDB, swapping a LevelDB and flat-file hybrid for SQLite. Chrome 156, which the Chrome 156 release notes list as reaching stable on October 20, 2026, includes the change in its feature list.

The web-facing API does not change. But "internal rewrite" is not the same as "nothing to check," and the details of the rollout matter for anyone who ships a storage-heavy product.

What is actually changing

According to the release notes, Chromium's IndexedDB implementation transitions to SQLite, replacing the previous LevelDB hybrid. The stated goal is modest: the change "is expected to improve reliability and, to a lesser extent, performance." So reliability is the headline, and speed is a secondary effect rather than the promise.

Two scoping details in the release notes are important. The change currently applies to new data stores only, and existing LevelDB data is unaffected during this rollout phase. In other words, this is not a flag day where every user's database is migrated overnight.

How the rollout got here

This did not appear out of nowhere. Chromium announced the work in a blink-dev web-facing change PSA that covered a first phase limited to in-memory contexts, meaning Incognito mode in Chromium and Google Chrome. That phase targeted Chrome 144 on desktop and Android and used A/B testing rather than a waterfall rollout. The PSA said the full migration for persistent, on-disk storage would follow after the team gathered reliability and performance data, and that a further PSA would accompany it because persistent storage carries higher risk.

Two points from that PSA are worth sitting with:

  • The migration introduces "a web-visible behavioral change concerning an edge case in IDB transaction scheduling." The new behavior aligns Chromium with Firefox and Safari. Both the old and new approaches are compliant with the spec.
  • The team acknowledged that the difference creates a temporary Incognito-detection signal, since transaction behavior differs between modes, while noting that other Incognito signals already exist.

Read together, the picture is a staged migration: in-memory first, new on-disk stores next, and legacy data later.

Why this is not a performance story (yet)

It is tempting to read "SQLite" and expect a benchmark chart. Resist that. The official wording is that performance improves only "to a lesser extent," and no numbers are given in the sources above. If you have seen speedups quoted for IndexedDB in Chrome, they most likely come from a different, earlier change: Chrome has been compressing IndexedDB values with Snappy since Chrome 129, which Chrome's documentation says can make some operations two to three times faster in synthetic benchmarks, with roughly 75 percent faster results for 1 MB structured payloads. That is an optimization of the old engine, and it applies to new data only.

Do not attribute those figures to the SQLite backend. Measure your own workload instead.

What to audit in your app

The practical risk is not the API surface. It is code that accidentally depends on the order in which transactions are scheduled. Because the PSA describes a changed edge case that brings Chromium in line with Firefox and Safari, apps that already pass their tests in all three engines are the least likely to be affected. Apps tested only in Chrome are the most likely.

1. Do not rely on cross-transaction ordering

If one part of your code opens a transaction and another part opens an overlapping one, do not assume which runs first unless the spec guarantees it. Make the dependency explicit by doing related work in one transaction, or by awaiting the first transaction's completion before starting the second.

function put(db, storeName, value) {
  return new Promise((resolve, reject) => {
    const tx = db.transaction(storeName, "readwrite");
    tx.objectStore(storeName).put(value);
    // Resolve on transaction completion, not on request success,
    // so the next step really starts after the data is committed.
    tx.oncomplete = () => resolve();
    tx.onerror = () => reject(tx.error);
    tx.onabort = () => reject(tx.error ?? new DOMException("Aborted", "AbortError"));
  });
}

await put(db, "drafts", { id: "a1", body: "..." });
await put(db, "outbox", { id: "a1", op: "sync" }); // ordered, not racing

2. Batch related writes into one transaction

If two writes must be seen together, they belong in the same transaction. This is correct under any engine and it is also the cheapest way to write, because you pay the commit cost once.

const tx = db.transaction(["drafts", "outbox"], "readwrite");
tx.objectStore("drafts").put({ id: "a1", body: "..." });
tx.objectStore("outbox").put({ id: "a1", op: "sync" });
await new Promise((res, rej) => {
  tx.oncomplete = res;
  tx.onerror = () => rej(tx.error);
});

3. Test outside Chrome, and in Incognito

Run your storage integration tests in Firefox and Safari. If they pass there, you are already on the behavior Chromium is converging toward. Also run them in a Chrome Incognito window, since that is where the new engine has been rolling out via A/B testing and where any scheduling-dependent bug would have surfaced first.

4. Do not build on the Incognito difference

The PSA is explicit that the transaction-behavior difference creates a temporary detection signal. It is not an API, it is not stable, and it will go away as the migration completes. Do not use it for feature gating.

5. Watch your telemetry, not just your tests

If you already collect error reports, tag IndexedDB failures such as AbortError, QuotaExceededError, and UnknownError by Chrome major version. A regression tied to a specific release is far easier to spot with that dimension than without it.

What we would do this week

  1. Grep your codebase for IndexedDB access outside a single storage module. Scattered indexedDB.open and db.transaction calls are where ordering assumptions hide.
  2. Run the storage test suite in Chrome Beta, Firefox, and Safari. Treat any Chrome-only pass as a bug in the test or in the code.
  3. Make writes await oncomplete, and group dependent writes into one transaction.
  4. Add the Chrome major version to your client error reports.
  5. Subscribe to the blink-dev announcements. The PSA says another one will accompany the persistent-storage migration, and that is the one to read closely.

The takeaway

A storage engine swap is the kind of change that rewards boring engineering. The API is stable, the rollout is staged, and the stated aim is reliability. If your IndexedDB code is already disciplined about transaction boundaries and tested across engines, you should see nothing. If it is not, the next few Chrome releases are a cheap moment to find out, before the migration reaches the data your users already have.

Sources: Chrome 156 release notes, blink-dev PSA on the IndexedDB SQLite backend, Chrome for Developers: More efficient IndexedDB storage.

←Back to all posts
Next step

Need help shipping software?

Tell us what you're trying to build. A discovery call, a one-page summary within 48 hours, a proposal within a week.

Response · 48h·NDA on request·US contracts only