Browser Storage Compared: localStorage, IndexedDB, Cache API, and OPFS

技术架构(Updated Jun 8, 2026)

Overview of Five Browser Storage Mechanisms

Modern browsers offer 5 primary storage mechanisms, each designed for different goals and use cases:

Storage Data Type Capacity Limit Persistence Thread Access
localStorage String key-value ~5-10 MB Permanent Main thread only
sessionStorage String key-value ~5-10 MB Tab close Main thread only
IndexedDB Structured data + Blob Hundreds of MB~unlimited Permanent Main thread + Worker
Cache API Request/Response pairs Shared with IndexedDB quota Permanent Main thread + Worker
OPFS File system Shared with IndexedDB quota Permanent Worker only (sync)

localStorage and sessionStorage

Basic Usage

localStorage.setItem('theme', 'dark');
const theme = localStorage.getItem('theme');
localStorage.removeItem('theme');
localStorage.clear();

for (let i = 0; i < localStorage.length; i++) {
  const key = localStorage.key(i);
  console.log(key, localStorage.getItem(key));
}

Key Differences

Feature localStorage sessionStorage
Lifetime Permanent Cleared on tab close
Scope Shared across same-origin tabs Current tab only
Typical use User preferences, tokens Form drafts, page state

Capacity Detection

function testLocalStorageQuota() {
  const testKey = '__quota_test__';
  let data = '';
  try {
    for (let i = 0; i < 1024; i++) data += 'a'.repeat(1024);
    for (let mb = 1; mb < 20; mb++) {
      localStorage.setItem(testKey, data.repeat(mb));
    }
  } catch (e) {
    console.log('localStorage quota exceeded');
  } finally {
    localStorage.removeItem(testKey);
  }
}

Caveats

  • Synchronous blocking: Read/write blocks the main thread; large data hurts performance
  • Strings only: Objects need JSON.stringify/JSON.parse
  • Storage event: Fires storage event in other same-origin tabs on modification

IndexedDB

Basic Usage

const openRequest = indexedDB.open('myDatabase', 1);

openRequest.onupgradeneeded = (event) => {
  const db = event.target.result;
  const store = db.createObjectStore('users', { keyPath: 'id' });
  store.createIndex('name', 'name', { unique: false });
};

openRequest.onsuccess = (event) => {
  const db = event.target.result;
  const tx = db.transaction('users', 'readwrite');
  const store = tx.objectStore('users');

  store.add({ id: 1, name: 'Alice', email: 'alice@example.com' });

  store.get(1).onsuccess = (e) => {
    console.log(e.target.result);
  };
};

Advanced Features

Feature Description
Transactions Guarantees atomicity, read/write separation
Indexes Efficient queries on non-key fields
Cursors Iterate large datasets without loading all at once
Blob storage Store File and Blob objects directly
Worker access Available in Service Workers and Web Workers

Simplified with the idb Library

import { openDB } from 'idb';

const db = await openDB('myDatabase', 1, {
  upgrade(db) {
    db.createObjectStore('users', { keyPath: 'id' });
  }
});

await db.put('users', { id: 1, name: 'Alice' });
const user = await db.get('users', 1);
const allUsers = await db.getAll('users');
await db.delete('users', 1);

Cache API

Basic Usage

const cache = await caches.open('v1');

await cache.put('/api/data', new Response(JSON.stringify({ foo: 'bar' })));

const response = await cache.match('/api/data');
const data = await response.json();

await cache.delete('/api/data');

With Service Worker

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((cached) => {
      if (cached) return cached;
      return fetch(event.request).then((response) => {
        const clone = response.clone();
        caches.open('v1').then((cache) => {
          cache.put(event.request, clone);
        });
        return response;
      });
    })
  );
});

Caching Strategies Compared

Strategy Description Use Case
Cache First Cache first, network on miss Static assets, fonts
Network First Network first, cache fallback API data, frequently updated content
Stale While Revalidate Return cache, update in background Non-critical APIs, images
Cache Only Cache only Offline pages, precached resources
Network Only Network only Non-GET requests, real-time data

OPFS (Origin Private File System)

Basic Usage

const root = await navigator.storage.getDirectory();

const fileHandle = await root.getFileHandle('data.bin', { create: true });
const writable = await fileHandle.createWritable();
await writable.write(new Uint8Array([1, 2, 3, 4]));
await writable.close();

const file = await fileHandle.getFile();
const buffer = await file.arrayBuffer();

Synchronous Access in Workers

// In a Worker — synchronous OPFS (extremely fast)
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle('large.bin', { create: true });
const accessHandle = await fileHandle.createSyncAccessHandle();

const buffer = new ArrayBuffer(4);
accessHandle.write(buffer, { at: 0 });
accessHandle.flush();
accessHandle.close();

OPFS vs File System Access API

Feature OPFS File System Access API
User permission Not required User must pick directory
Visibility Browser-internal virtual FS Real OS file system
Worker sync Supported Not supported
Persistence Permanent Depends on user grant
Use case High-performance temp file processing File editors, export

Storage Quota and Persistence

Query Available Quota

const estimate = await navigator.storage.estimate();
console.log(`Used: ${(estimate.usage / 1024 / 1024).toFixed(2)} MB`);
console.log(`Quota: ${(estimate.quota / 1024 / 1024).toFixed(2)} MB`);

Request Persistence

const persisted = await navigator.storage.persist();
if (persisted) {
  console.log('Storage persisted, will not be auto-evicted');
} else {
  console.log('Storage may be cleared under pressure');
}

Browsers may auto-evict non-persistent storage under pressure. Call navigator.storage.persist() to request persistence.


Decision Guide

What do you need to store?
├── Simple key-value (< 5MB)
│   ├── Cross-tab sharing → localStorage
│   └── Current tab only → sessionStorage
├── Structured data / many records
│   └── IndexedDB
├── HTTP request/response caching
│   └── Cache API
└── Large files / binary / high-performance I/O
    └── OPFS

Real-World Examples

Scenario Recommended Storage Reason
User theme preference localStorage Simple key-value, cross-page
Form draft sessionStorage Temporary, tab-scoped
Offline tool data IndexedDB Structured, indexed queries
Static asset cache Cache API Designed for Request/Response
Video processing temp files OPFS Worker sync I/O, high performance

Security Considerations

  • XSS risk: localStorage is readable by XSS — never store sensitive tokens
  • HttpOnly cookies: Auth tokens should use HttpOnly + Secure cookies
  • Storage encryption: Encrypt sensitive data with JWT or Hash before storing
  • Same-origin isolation: All storage follows same-origin policy; cross-origin access is blocked

Summary

Browser storage has evolved from a single localStorage to a complete system covering key-value pairs, structured data, HTTP caching, and file systems. The key to selection is matching data characteristics: simple config → localStorage, structured data → IndexedDB, HTTP caching → Cache API, high-performance file I/O → OPFS.

Use the Cookie Parser to inspect cookie configuration, the JWT Tool to verify token security, and the Hash Tool to hash sensitive data.

Try these browser-local tools — no sign-up required →

#localStorage#sessionStorage#IndexedDB#Cache API#存储对比