Notepad Editor/Blog/IndexedDB Quotas and Eviction Realities in 2026: Chrome vs Safari vs Firefox
Blog

IndexedDB Quotas and Eviction Realities in 2026: Chrome vs Safari vs Firefox

Comprehensive guide to browser storage quotas, Safari WebKit 7-day ITP eviction rules, navigator.storage.persist(), and safely persisting gigabytes of offline notes.

Engineering & Architecture12 min readMar 11, 2026By QNotepad Team
IndexedDB Storage Limits and Eviction Rules across Chrome, Safari, and Firefox

IndexedDB in 2026 is capable of storing tens of gigabytes of structured offline data per origin on desktop and mobile browsers. However, actual data durability varies dramatically between Chromium (Chrome, Edge, Brave), WebKit (Safari on macOS and iOS), and Gecko (Firefox).

While Chrome grants web applications access to up to 60% of total available disk space, Safari enforces a strict 7-day eviction timer on script-writable storage under Intelligent Tracking Prevention (ITP) unless specific interaction conditions are met.

For engineers and knowledge workers relying on local-first web notepads, offline progressive web apps (PWAs), or browser-based developer tools, understanding how browser storage engines allocate quotas, evict databases under disk pressure, and handle the navigator.storage.persist() permission is critical to preventing accidental data loss.

This guide provides an exhaustive, benchmarked breakdown of modern browser storage architectures, quota formulas, eviction algorithms, and copy-pasteable TypeScript strategies for bulletproof offline data durability.


The Three Tiers of Web Storage: Beyond the 5 MB Myth

A pervasive misconception among web developers is that "browser storage is capped at 5 MB."

This misconception stems from the legacy window.localStorage API, standardized in HTML5 more than fifteen years ago. In 2026, modern web browsers feature three distinct storage tiers, each engineered for completely different workloads:

+-------------------------------------------------------------------------+
| Modern Browser Client-Side Storage Tiers                               |
+-------------------------------------------------------------------------+
| Tier 1: LocalStorage / SessionStorage                                  |
|   Capacity: ~5 MB to 10 MB per origin                                   |
|   I/O Model: Synchronous, main-thread blocking                          |
|   Data Structure: Key-value string pairs only                           |
|   Ideal For: UI theme preferences, temporary auth tokens                |
+-------------------------------------------------------------------------+
| Tier 2: IndexedDB (IDB)                                                 |
|   Capacity: 1 GB to 100+ GB (Disk-fraction based)                       |
|   I/O Model: Asynchronous transactional Indexed Records                 |
|   Data Structure: Structured cloneable objects, Blobs, ArrayBuffers    |
|   Ideal For: Local-first offline notes, document stores, media caches   |
+-------------------------------------------------------------------------+
| Tier 3: Origin Private File System (OPFS)                               |
|   Capacity: Shared origin pool (tens to hundreds of GBs)                |
|   I/O Model: In-worker synchronous fast binary file handles             |
|   Data Structure: Raw hierarchical virtual filesystem                   |
|   Ideal For: SQLite WASM databases, audio/video editing engines         |
+-------------------------------------------------------------------------+

For a local-first notepad or text editor, IndexedDB is the ideal sweet spot: it supports asynchronous non-blocking transactions, indexes multiple fields (such as note creation timestamps, tags, and search keywords), and handles hundreds of thousands of documents without degrading UI responsiveness.


Browser Storage Quotas and Eviction Rules: Chrome vs Safari vs Firefox

Browser storage is governed by the W3C StorageManager API. However, each major browser engine implements its own quota calculations, eviction policies, and user consent gates.

1. Chromium Engine (Google Chrome, Microsoft Edge, Brave, Opera)

Chromium utilizes a shared storage pool architecture managed by its storage/browser/quota subsystem.

  • Global Pool Quota: Chromium allows the entire web platform to consume up to 80% of total available disk space.
  • Per-Origin Quota: A single origin (such as qnotepad.com) can consume up to 60% of total disk space (or a hard cap based on system architecture). On a 512 GB SSD with 300 GB free, a single web app can store upwards of 150 GB in IndexedDB.
  • Eviction Mode: By default, an origin is placed into the Best-Effort storage tier. Under extreme storage pressure (when free disk space drops below 1 GB or the global pool limit is exceeded), Chromium evicts origins using a Least Recently Used (LRU) algorithm. The origin that has gone the longest without user access is deleted entirely.
  • Persistent Storage Gate: Web applications can call navigator.storage.persist(). If granted, the origin transitions to the Persistent tier and is completely exempt from automatic LRU eviction. Chrome grants persistence automatically if the app is installed as a PWA, has high user engagement scores (Site Engagement index), or is bookmarked.

2. WebKit Engine (Safari macOS, iOS, iPadOS)

Safari's storage architecture is heavily influenced by Apple's privacy model and Intelligent Tracking Prevention (ITP).

  • Per-Origin Quota Floor: Safari allocates a baseline origin quota starting at 1 GB.
  • Dynamic Quota Prompts: When an application attempts to exceed 1 GB, Safari halts the write operation and displays a system dialog: "qnotepad.com" would like to use up to 2 GB of storage on your Mac. If approved by the user, the quota increments in powers of two (2 GB, 4 GB, etc.).
  • The Safari ITP 7-Day Eviction Trap: This is the single most critical eviction rule in web development. Under WebKit's ITP, all client-side writable storage (IndexedDB, CacheStorage, localStorage) is deleted after 7 days of Safari use without user interaction on that origin.
    • If a user takes notes in a web notepad, bookmarking it or keeping it open, the timer resets on each visit.
    • However, if the user navigates other websites for 7 days without opening the notepad tab, Safari's background cleaning process can wipe the IndexedDB database to prevent cross-site fingerprinting.
    • Exception: Adding the web app to the iOS Home Screen (as a standalone Web Clip / PWA) or installing it on macOS grants it standalone status, effectively shielding it from aggressive 7-day ITP purges.
  • Persistence Support: Safari supports navigator.storage.persist(), but its internal evaluation requires explicit user interaction or Home Screen installation.

3. Gecko Engine (Mozilla Firefox)

Firefox provides a balance between generous disk allocations and strict user prompt controls.

  • Group Quota: An origin group (the domain and its subdomains) can consume up to 20% of the global storage limit (which is roughly 50% of available disk space). On modern computers, this allows 5 GB to 20 GB of IndexedDB data.
  • Eviction Mode: Similar to Chromium, Firefox treats unprivileged storage as best-effort LRU. When disk capacity reaches 90% utilization, Firefox purges the oldest origins.
  • Persistent Storage Gate: When a website invokes navigator.storage.persist(), Firefox displays an explicit browser prompt to the user: "Should https://qnotepad.com be allowed to store data on your computer for persistent use?". Once the user clicks "Allow", the origin is never evicted automatically.

Detailed Comparison: Cross-Browser Storage Matrix

The following benchmark table synthesizes the storage behaviors across all four major browser configurations in 2026:

Browser / Platform Per-Origin Max Quota Eviction Policy Safari ITP 7-Day Trap? navigator.storage.persist() Behavior Private / Incognito Mode IDB
Chrome (Desktop & Android) Up to 60% of disk space (100+ GB) Best-Effort (LRU) under disk pressure No Auto-granted based on engagement & PWA install In-memory only; wiped on tab/window close
Edge (Windows & macOS) Up to 60% of disk space Best-Effort (LRU) No Auto-granted via engagement score In-memory only; wiped on close
Safari (macOS) 1 GB default (Prompts for 2GB+) LRU + 7-day inactive purge Yes (7 days) Silent grant or requires Home Screen install Blocked or in-memory depending on OS version
Safari (iOS / iPadOS) 1 GB default (Strict memory constraints) Aggressive OS memory pressure purge Yes (7 days) Requires "Add to Home Screen" for durability Strictly isolated in-memory; wiped on exit
Firefox (Desktop) Up to 20% of disk space (5-20 GB) Best-Effort (LRU) No Explicit permission modal prompt to user Disabled or mock memory store

Demystifying navigator.storage.persist()

By default, every byte you write to IndexedDB is classified as "best-effort" (temporary).

This means the browser reserves the legal right to delete your database without warning if the host operating system signals that disk space is dangerously low (for example, during a system OS update or massive game download).

To achieve true data durability, a local-first application must request "persistent" storage classification.

// Step 1: Check if storage is already persistent
if (navigator.storage && navigator.storage.persisted) {
  const isPersisted = await navigator.storage.persisted();
  console.log(`Current storage tier: ${isPersisted ? "Persistent" : "Best-Effort"}`);
}

// Step 2: Request elevation to Persistent tier
if (navigator.storage && navigator.storage.persist) {
  const granted = await navigator.storage.persist();
  if (granted) {
    console.log("Storage elevated to Persistent tier. Protected from LRU eviction.");
  } else {
    console.warn("Browser denied persistent storage. Subject to LRU eviction under disk pressure.");
  }
}

When Does the Browser Say "Yes"?

Browsers do not grant persistence unconditionally. They evaluate heuristics to prevent malicious or spammy websites from hoarding disk space:

  1. User Engagement: Does the user visit this site frequently? (Chrome uses the SiteEngagementScore).
  2. Installation Status: Is the application added to the desktop dock, taskbar, or mobile Home Screen as a PWA?
  3. Notification or Background Sync Permissions: Has the user granted other elevated browser privileges?
  4. Direct Interaction: Was persist() triggered inside a user gesture (such as clicking an "Enable Offline Mode" or "Save Note" button)?

Production Implementation: The Resilient Storage Manager

Below is a production-grade TypeScript class that wraps the modern Web Storage API. It handles quota estimation, transparently requests persistent storage, detects low-disk warnings, and catches QuotaExceededError exceptions before they crash client-side editors:

/**
 * Safe Browser Storage Manager
 * Detects quotas, requests persistence, and protects local-first notes.
 */

export interface StorageHealthReport {
  isPersisted: boolean;
  usedBytes: number;
  totalBytes: number;
  usagePercentage: number;
  isLowSpace: boolean;
}

export class BrowserStorageManager {
  /**
   * Retrieves the current origin storage consumption and persistence status.
   */
  public static async getHealthReport(): Promise<StorageHealthReport> {
    if (!navigator.storage || !navigator.storage.estimate) {
      return {
        isPersisted: false,
        usedBytes: 0,
        totalBytes: 0,
        usagePercentage: 0,
        isLowSpace: false,
      };
    }

    // 1. Query browser storage estimate
    const estimate = await navigator.storage.estimate();
    const usedBytes = estimate.usage ?? 0;
    const totalBytes = estimate.quota ?? 0;
    const usagePercentage = totalBytes > 0 ? (usedBytes / totalBytes) * 100 : 0;

    // 2. Query persistence tier
    let isPersisted = false;
    if (navigator.storage.persisted) {
      isPersisted = await navigator.storage.persisted();
    }

    // Low space warning threshold: 90% of origin quota or under 50 MB remaining
    const remainingBytes = totalBytes - usedBytes;
    const isLowSpace = usagePercentage >= 90 || (totalBytes > 0 && remainingBytes < 50 * 1024 * 1024);

    return {
      isPersisted,
      usedBytes,
      totalBytes,
      usagePercentage,
      isLowSpace,
    };
  }

  /**
   * Prompts the browser to elevate origin storage to the Persistent tier.
   * Call this during explicit user actions (e.g. clicking "Enable Offline Autosave").
   */
  public static async requestPersistence(): Promise<boolean> {
    if (!navigator.storage || !navigator.storage.persist) {
      return false;
    }

    try {
      const alreadyPersisted = await navigator.storage.persisted();
      if (alreadyPersisted) return true;

      return await navigator.storage.persist();
    } catch (err) {
      console.error("Failed to request persistent browser storage:", err);
      return false;
    }
  }

  /**
   * Safe wrapper around IndexedDB write transactions to catch quota exhaustion.
   */
  public static async executeSafeWrite<T>(writeOperation: () => Promise<T>): Promise<T> {
    try {
      return await writeOperation();
    } catch (error: any) {
      if (
        error.name === "QuotaExceededError" ||
        error.code === 22 ||
        error.number === -2147024882
      ) {
        // Trigger emergency backup or alert user
        console.error("FATAL: IndexedDB QuotaExceededError encountered. Disk is full.");
        throw new Error(
          "Your browser storage quota has been exceeded. Please export your notes to Markdown or clear unused disk space."
        );
      }
      throw error;
    }
  }

  /**
   * Helper to format raw byte counts into human-readable strings (MB, GB).
   */
  public static formatBytes(bytes: number, decimals = 1): string {
    if (bytes === 0) return "0 B";
    const k = 1024;
    const sizes = ["B", "KB", "MB", "GB", "TB"];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + " " + sizes[i];
  }
}

Common Pitfalls and Failure Modes in Local-First Storage

Even experienced engineers regularly stumble over edge cases in browser storage lifecycle management:

1. The Incognito / Private Browsing Trap

In Chrome, Firefox, and Safari, opening an incognito window creates an ephemeral, in-memory IndexedDB instance.

  • Notes created during an incognito session function smoothly and pass read/write tests.
  • The Catastrophe: As soon as the user closes the incognito window, the entire database is wiped from RAM immediately. No persistence request can override this.
  • Defense: Detect incognito mode or display a prominent UI banner when running in private contexts: "Private Mode detected: notes will not be saved after this tab closes."

2. Multi-Tab Transaction Deadlocks

If a user opens your notepad across seven browser tabs, each tab establishes a connection to the same IndexedDB database.

  • If you push a database version upgrade (e.g., adding an index in Dexie or raw IDB onupgradeneeded), the upgrade transaction will block indefinitely until all other six tabs close or reload their connections.
  • Defense: Always register a listener for versionchange events on your database connection and close inactive connections gracefully:
db.onversionchange = () => {
  db.close();
  alert("A newer version of this application was loaded in another tab. Please refresh.");
};

3. Relying Solely on Local Storage Without Export Hooks

Treating any browser database as a permanent physical archive without providing effortless manual export mechanisms is a critical architectural error. Operating system updates, accidental browser data clearances, or device replacements will eventually separate the user from their browser cache.

  • Defense: Pair IndexedDB local-first storage with immediate export features: one-click Download as Markdown (.md), Print to PDF, and full JSON Notebook Backup.

Architectural Guidelines for Building Durable Web Notepads

To ensure zero data loss for users in 2026, adhere to these five architectural rules:

  1. Write Asynchronously to IndexedDB on Every Pause: Debounce user keystrokes by 300 milliseconds. Never wait for an explicit "Save" button.
  2. Prompt for navigator.storage.persist() After Value Creation: Do not request persistence on page load (browsers will reject it). Prompt the user after they create their second note or write their first 100 words.
  3. Monitor navigator.storage.estimate() Periodically: Calculate remaining quota and issue a gentle non-blocking warning when utilization crosses 80%.
  4. Encourage "Add to Home Screen" on iOS: For Apple devices, guide mobile users to add the web app to their Home Screen. This transitions the app out of Safari's 7-day ITP eviction sandbox into a durable Web Clip container.
  5. Implement Zero-Knowledge Cloud Backup: For users who require cross-device continuity without sacrificing privacy, provide end-to-end encrypted backup where client databases are sealed with AES-GCM before reaching server storage.

Technical FAQs: IndexedDB Storage Limits

How much data can IndexedDB store in Google Chrome?

On modern desktop systems, Chrome allows web applications to consume up to 60% of total free disk space per origin. On a computer with 200 GB of free space, a single web app can store up to 120 GB. On Android, the limit is typically 2 GB to 10 GB depending on available internal flash memory.

Will clearing my browser history delete my IndexedDB notes?

It depends on which checkbox is selected. In Chrome and Firefox, clearing "Browsing History" alone does not delete IndexedDB data. However, selecting "Cookies and other site data" or "Clear all site data" will purge all IndexedDB databases, localStorage, and cached service worker assets.

How do I prevent Safari on iOS from deleting my notes after 7 days?

Under Apple's WebKit ITP rules, script-writable storage is subject to eviction if the user does not visit the website for 7 days. To protect your data, open the website in Safari, tap the Share button, and select "Add to Home Screen". Standalone home-screen web applications are treated with higher durability privileges.

Can a website fill up my entire hard drive without asking?

No. All major browsers enforce global storage pool caps (typically 50% to 80% of total disk space) and per-origin caps. Furthermore, if an application attempts to write data when the host system disk has less than 1 GB of free space, the browser throws a QuotaExceededError and halts all write operations.

Is IndexedDB faster than localStorage?

For large datasets, yes. LocalStorage is completely synchronous and runs on the browser's main UI thread. Writing a 2 MB string to localStorage can block DOM rendering and cause user cursor stutter. IndexedDB runs on a separate browser storage thread with asynchronous transactional I/O, ensuring that saving large documents never drops UI frames.

Tags:#indexeddb#browser-storage#safari-itp#web-storage-quotas#local-first#offline-storage#javascript
EXPERIENCE QNOTEPAD

Test Offline IndexedDB Storage in QNotepad ->

No login, no cookies wall, no servers looking over your shoulder. Write locally with zero latency.

Test Offline IndexedDB Storage in QNotepad ->

Related Reading & Architecture Guides

View all guides