
Stop wrestling with localStorage size limits and IndexedDB's async maze. The web finally has a real database solution: SQLite running natively in browsers through OPFS (Origin Private File System). This isn't another wrapper library—it's the actual SQLite engine, compiled to WebAssembly, with full relational database capabilities.
The Storage Problem We've All Lived With
For years, client-side storage has been a compromise. localStorage caps out at 5-10MB and only handles key-value pairs. IndexedDB offers more space but forces you into callback hell or complex wrapper libraries. Both leave you missing the power of SQL—joins, indexes, transactions, and the relational model that actually matches your data.
Meanwhile, your native apps enjoy SQLite's battle-tested reliability. Until now, web developers were stuck with inferior alternatives.
OPFS: The Game Changer
Origin Private File System (OPFS) changes everything. It provides synchronous file operations in Web Workers, enabling SQLite to run with true persistence. Unlike previous browser-based SQLite attempts that relied on slow, async operations, OPFS delivers near-native performance.
<> "OPFS uses synchronous file operations via createSyncAccessHandle(), enabling high-performance in-place reads and writes that rival native SQLite performance."/>
The key breakthrough is SQLite 3.40+'s official OPFS Virtual File System (VFS). This isn't a community hack—it's officially supported by the SQLite team, ensuring long-term stability.
Getting Started: Your First Browser Database
Here's how to spin up SQLite with OPFS persistence:
1// In a dedicated Web Worker
2importScripts('sqlite3.js'); // Load SQLite WASM
3
4const start = async () => {
5 const sqlite3 = await sqlite3InitModule();
6
7 // Open database with OPFS persistence
8 const db = new sqlite3.oo1.OpfsDb('/myapp.db?mode=rwc&vfs=opfs');Critical requirement: Your server must serve these headers for SharedArrayBuffer support:
1Cross-Origin-Opener-Policy: same-origin
2Cross-Origin-Embedder-Policy: require-corpWithout these, SQLite falls back to slower async operations.
Real-World Performance: The Notion Case Study
Notion's engineering team documented their OPFS SQLite implementation, reporting 10x+ performance improvements for their caching layer. They handle multiple tabs by using SharedWorkers with a connection pool, allowing concurrent reads while serializing writes to prevent database corruption.
Their architecture uses Comlink to proxy database operations from the main thread to SharedWorkers:
1// Main thread - proxy to SharedWorker
2import * as Comlink from 'comlink';
3
4const worker = new SharedWorker('db-worker.js');
5const db = Comlink.wrap(worker.port);
6
7// This runs in the SharedWorker with SQLite
8await db.query('SELECT * FROM documents WHERE workspace_id = ?', [workspaceId]);This pattern lets multiple tabs share the same database connection while maintaining data consistency.
Handling Browser Compatibility
OPFS isn't universal yet. Chrome and Edge have full support, Firefox is progressing, and Safari lags behind. More importantly, incognito/private browsing modes have restrictions—Chrome limits OPFS to ~100MB, while Firefox and Safari disable it entirely.
Smart apps detect OPFS availability and fall back gracefully:
1const initDatabase = async () => {
2 const sqlite3 = await sqlite3InitModule();
3
4 // Check if OPFS is available
5 const hasOPFS = sqlite3.capi.sqlite3_vfs_find("opfs");
6
7 if (hasOPFS) {
8 // Use persistent OPFS storage
9 return new sqlite3.oo1.OpfsDb('/app.db?vfs=opfs');
10 } else {
11 // Fall back to IndexedDB VFS (slower but works everywhere)
12 return new sqlite3.oo1.DB('/app.db', 'c');
13 }
14};The IndexedDB fallback loses some performance but maintains the same SQLite API, keeping your application code unchanged.
Debugging Your Browser Database
OPFS stores files in a sandboxed filesystem invisible to normal DevTools. Install Chrome's OPFS Explorer extension to inspect your database files directly. You can export the SQLite file and open it in any standard SQLite browser for full schema and data inspection.
For debugging queries, SQLite WASM provides the same .explain query plan and profiling tools you'd use server-side.
The Architectural Shift
This isn't just about better storage—it's about rethinking web app architecture. With client-side SQLite, you can:
- Build offline-first apps that sync when connected, not apps that break without network
- Eliminate API roundtrips for filtering, sorting, and aggregating data you already have
- Use actual database patterns like migrations, indexes, and foreign key constraints
- Leverage decades of SQL optimization instead of reinventing query logic in JavaScript
<> "SQLite WASM with OPFS unlocks offline-first applications with true relational database features—ACID transactions, WAL mode, virtual tables, and persistence across browser sessions."/>
Why This Matters Now
Web apps are eating the world, but they've been handicapped by storage limitations. OPFS SQLite removes that constraint. We're moving toward a web where applications can be as data-rich and responsive as native apps, with the deployment simplicity of HTML.
The implications extend beyond individual apps. This enables new categories of web software: local-first collaborative editors, offline-capable CRMs, and data visualization tools that don't need server infrastructure to handle complex queries.
Start experimenting now, even if you implement fallbacks for broader compatibility. The browsers supporting OPFS represent the majority of your users, and that percentage is only growing. Your future self—and your users—will thank you for ditching localStorage's limitations today.

