๐Ÿ’พ Chapter 48: Web Storage API

Quota Management & QuotaExceededError

Browser quota limits, UTF-16 byte memory calculations, `QuotaExceededError` exception handling, and automated LRU cache eviction.

LEARNING OBJECTIVES โŒต
  • Calculate the exact byte memory footprint of Web Storage data based on UTF-16 code units (2 bytes per character).
  • Understand quota allocations across major browser engines (Chromium, Gecko, WebKit).
  • Intercept and handle DOMException: QuotaExceededError without breaking application runtime state.
  • Design and implement an automated Least Recently Used (LRU) cache eviction algorithm for client-side storage.
๐ŸŽฌ INTERACTIVE VISUAL PIPELINE Core Architecture Simulation
๐ŸŒ
1. Input
Directives & Tags
โš™๏ธ
2. Parse
Tokenizer & AST
๐ŸŒณ
3. Layout
Box Model & Flow
๐ŸŽจ
4. Render
GPU Paint & Composite
PHASE 1: INPUT & DIRECTIVES
Browser receives declarative markup stream, parsing tag tokens and initializing component state.

๐Ÿ“– The Mental Model & Story (Intuitive Foundation)

Imagine a carry-on suitcase for an airplane flight that has a strict 5 kg weight limit. You can pack lightweight t-shirts, socks, and headphones with plenty of room to spare. But if you try to pack a 10 kg solid iron barbell, the airport luggage scale triggers a red alarm (QuotaExceededError) and the flight attendant refuses to let you board.

+---------------------------------------------------------------------------------------------------+
|  THE STORAGE QUOTA BOUNDARY                                                                       |
|                                                                                                   |
|  [ Current Storage Bucket: 4.8 MB consumed ]                                                      |
|  localStorage.setItem('huge_image_base64', '800KB payload')                                      |
|             |                                                                                     |
|             v  (Attempting write: Total would be 5.6 MB > 5.0 MB Limit!)                          |
|  ๐Ÿ’ฅ DOMException: Failed to execute 'setItem' on 'Storage': Setting the value... exceeded quota! |
|                                                                                                   |
|  * The write is REJECTED atomically. No partial data is written.                                  |
|  * Solution: Implement LRU Eviction to discard oldest items before writing!                       |
+---------------------------------------------------------------------------------------------------+

Without proactive quota management, your application will crash the moment an active user exceeds their local storage limit. By building an LRU (Least Recently Used) eviction engine, your application becomes self-cleaningโ€”automatically shedding old, unused cached data to make room for fresh items.


Technical Deep Dive & Specifications

Browser Quota Comparison & Storage Allocations

The WHATWG specification recommends a default quota of 5 MB per origin, but does not enforce a rigid ceiling. Different browser vendors implement slightly different limits:

Browser Engine Typical localStorage Quota Typical sessionStorage Quota Quota Scope
Google Chrome / Chromium ~5 MB โ€“ 10 MB ~5 MB โ€“ 10 MB Per Origin (<scheme, host, port>)
Mozilla Firefox (Gecko) ~5 MB โ€“ 10 MB ~5 MB โ€“ 10 MB Per Origin
Apple Safari (WebKit) ~5 MB ~5 MB Per Origin (Subject to 7-day ITP caps)
Mobile Safari (iOS) ~5 MB ~5 MB Per Origin (Restricted on low memory)

The UTF-16 Memory Model (2 Bytes Per Character)

In JavaScript and the DOM WebIDL specification, DOMString values are encoded in UTF-16 code units. Each character in a storage key or value consumes 2 bytes (16 bits) of allocated memory.

$$\text{Total Memory (Bytes)} = \sum (\text{length of Key} + \text{length of Value}) \times 2$$

Therefore:

  • A 5 MB quota allows roughly 2,621,440 characters (approx. 2.5 million characters of text).
  • Storing high-density binary data as Base64 strings inflates raw payload size by 33%, which is then doubled by UTF-16 encoding, rapidly consuming available storage.

QuotaExceededError Across Browser Engines

When storage.setItem() attempts to write a payload that exceeds the remaining origin quota, the browser throws a DOMException.

try {
  localStorage.setItem('my_key', largePayload);
} catch (error) {
  if (
    error instanceof DOMException &&
    (
      // Standard W3C/WHATWG name
      error.name === 'QuotaExceededError' ||
      // Legacy code check (W3C standard code 22)
      error.code === 22 ||
      // Firefox legacy name
      error.name === 'NS_ERROR_DOM_QUOTA_REACHED' ||
      error.code === 1014
    )
  ) {
    console.error('Storage quota exceeded! Executing cache eviction...');
  }
}

The LRU (Least Recently Used) Eviction Architecture

An LRU cache tracks when items are accessed or written. When storage space runs out, the eviction engine deletes the item with the oldest access timestamp until sufficient space is freed.

       Write New Item -> [ Storage Full? ]
                                |
                   +------------+------------+
                   |                         |
               NO (Space OK)             YES (Full)
                   |                         |
               Write to Disk                 v
                                  Find Oldest 'lastAccessed'
                                             |
                                     Evict Oldest Key
                                             |
                                  Retry Write Operation
+------------------------------------------------------------------------------------+
|  LRU CACHE METADATA STRUCTURE                                                      |
|  Key: "cache:user:101"                                                             |
|  Value: {                                                                          |
|    "lastAccessed": 1724200000000,  // Unix timestamp (ms)                          |
|    "data": { "name": "Alice", "role": "engineer" }                                 |
|  }                                                                                 |
+------------------------------------------------------------------------------------+

๐Ÿ’ป Interactive Code Playground

Starter Code

Line-by-Line Code Breakdown

  • Lines 73โ€“81 (calculateUsageBytes): Iterates through all stored keys and values, computing character count multiplied by 2 to measure total memory utilization in UTF-16 code units.
  • Lines 83โ€“91 (updateMeter): Calculates percentage consumed against an estimated 5MB boundary and drives the CSS gradient meter width dynamically.
  • Lines 94โ€“96 (create500KBString): Creates a 250,000-character string ('X'.repeat(250000)), which occupies exactly 500,000 bytes (approx. 500 KB) in memory.
  • Lines 100โ€“108: Traps err.name === 'QuotaExceededError' cleanly when localStorage.setItem() exceeds capacity, preventing runtime crashes.
  • Lines 111โ€“127 (btn-stress-test): Synchronously loops until setItem() throws, identifying the exact maximum storage boundary allocated by the host browser.

Expected Browser Render Output


SYS: ACTIVE
HULL: 98%
CORE: STABLE
NET: ONLINE
HTML STARSHIP CODE TERMINAL playground.html
LIVE RENDER & DIAGNOSTICS CORE TEMP: 45ยฐC
INSPECTING DOM: VALID
TAGS: SCANNING...
+-------------------------------------------------------------+
| Storage Quota Stress Tester                                 |
| Current Usage: 4,500.0 KB (4.39 MB) / ~5,120 KB (Est. 5MB)   |
| [======================================------] 88%          |
|                                                             |
| [Inject 500 KB Chunk] [Stress Test to Max] [Clear Test Data]|
|                                                             |
| Diagnostic Log:                                             |
| [02:26:15] Successfully wrote 500KB chunk: quota_test_chunk |
| [02:26:18] ๐Ÿšจ QUOTA EXCEEDED! Browser stopped writes at 51.  |
| [02:26:18] Total estimated stored capacity: 5.12 MB         |
+-------------------------------------------------------------+

๐Ÿ‹๏ธ Hands-On Exercise

๐ŸŽฏ The Challenge: Build an Automated Self-Evicting LRU Storage Manager

Build a production-ready cache wrapper class LRUStorageCache that stores items with a timestamp. When a QuotaExceededError occurs during a write, it automatically finds the oldest accessed key, deletes it, and retries the write operation until it succeeds.

Your Goal:

  1. Prefix cached keys with a configurable namespace (e.g. lru_cache:).
  2. Store every item wrapped in an envelope: { lastAccessed: Date.now(), data: payload }.
  3. When get(key) is called, update the item's lastAccessed timestamp.
  4. When set(key, data) throws QuotaExceededError, find the key in the namespace with the smallest lastAccessed timestamp, remove it, and retry writing.

๐Ÿ Starter Code Sandbox

SYS: ACTIVE
HULL: 98%
CORE: STABLE
NET: ONLINE
STARTER CODE SANDBOX exercise.html
LIVE RENDER & DIAGNOSTICS CORE TEMP: 45ยฐC
INSPECTING DOM: VALID
TAGS: SCANNING...

โš ๏ธ Common Pitfalls

  1. Base64 Bloat in Web Storage: Storing base64 images or PDFs in localStorage quickly triggers QuotaExceededError. Base64 is ~33% larger than raw binary, and JavaScript doubles this in memory via UTF-16. Use IndexedDB with Blob or ArrayBuffer instead.
  2. Assuming 10MB Everywhere: Never assume 10MB is guaranteed. On low-memory mobile devices or privacy browsers (Brave/Tor), storage limits can be restricted to 5MB or less.
  3. Unbounded Writes in Event Handlers: Storing continuous user coordinates (mousemove or scroll positions) without throttling will rapidly exhaust disk space.

๐Ÿ’ก Pro Tips

  1. The Storage API (navigator.storage.estimate): Use the modern navigator.storage.estimate() API to query overall origin storage quota and usage asynchronously before attempting large operations.
  2. Soft Quota Warning Thresholds: Set an in-app soft ceiling (e.g. 80% of capacity). When usage exceeds 4MB, trigger proactive background cleanup before a hard QuotaExceededError interrupts user workflows.

๐Ÿ“Œ Key Takeaways

  • Web Storage limits typically range from 5 MB to 10 MB per origin.
  • JavaScript strings in Web Storage use UTF-16 code units (2 bytes per character).
  • Writing beyond the available quota throws a DOMException named QuotaExceededError.
  • Writes that fail due to quota exhaustion are rejected atomically; no partial data is written.
  • Implementing an LRU (Least Recently Used) cache eviction algorithm ensures automatic cleanup and prevents application crashes.
  • --
โญ LEARN: HTML ๐ŸŒŸ โš”๏ธ QUIZ BATTLE ARENA // ACTIVE
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 1 / 3

How much disk/memory space does a 1,000,000-character ASCII string consume in Web Storage?

Question 1 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 2 / 3

What happens to existing localStorage data when a QuotaExceededError is thrown during a setItem() call?

Question 2 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 3 / 3

How does an LRU (Least Recently Used) cache decide which item to evict first when storage is full?

Question 3 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP