Chrome Extension Storage Quota Management — Best Practices

6 min read

Storage Quota Management

Chrome extension storage has strict quota limits that require careful management. This guide covers patterns for effectively managing storage quotas in your extension.

Quota Overview

Storage Type Quota Limit Notes
local 10 MB Per extension, not synced
sync 100 KB total, 8 KB/item Synced across user’s devices
session 10 MB Cleared on browser restart

Monitoring Usage

// Monitor storage usage
async function getStorageUsage() {
  const usage = await chrome.storage.local.getBytesInUse();
  const quota = 10 * 1024 * 1024; // 10 MB
  return { used: usage, available: quota - usage, percent: (usage / quota) * 100 };
}

Data Compression

Use LZ-string to compress JSON data before storing:

import LZString from 'lz-string';

const compress = (data) => LZString.compressToUTF16(JSON.stringify(data));
const decompress = (compressed) => JSON.parse(LZString.decompressFromUTF16(compressed));

Data Eviction Strategies

LRU Cache Implementation

class LRUStorage {
  constructor(maxItems = 100) {
    this.maxItems = maxItems;
    this.key = 'lru_cache';
  }

  async get(key) {
    const cache = await chrome.storage.local.get(this.key);
    const items = cache[this.key] || {};
    if (items[key]) {
      items[key].lastAccess = Date.now();
      await chrome.storage.local.set({ [this.key]: items });
      return items[key].data;
    }
    return null;
  }

  async set(key, value) {
    let cache = (await chrome.storage.local.get(this.key))[this.key] || {};
    if (Object.keys(cache).length >= this.maxItems) {
      const oldest = Object.entries(cache)
        .sort(([, a], [, b]) => a.lastAccess - b.lastAccess)[0][0];
      delete cache[oldest];
    }
    cache[key] = { data: value, lastAccess: Date.now() };
    await chrome.storage.local.set({ [this.key]: cache });
  }
}

TTL-Based Expiry

async function setWithTTL(key, value, ttlMs) {
  await chrome.storage.local.set({
    [key]: { data: value, expires: Date.now() + ttlMs }
  });
}

Splitting Large Data

For data exceeding single-key limits, chunk across multiple keys:

const CHUNK_SIZE = 100 * 1024; // 100 KB

async function storeLargeData(key, data) {
  const json = JSON.stringify(data);
  const chunks = [];
  for (let i = 0; i < json.length; i += CHUNK_SIZE) {
    chunks.push(json.slice(i, i + CHUNK_SIZE));
  }
  const storage = {};
  chunks.forEach((chunk, i) => storage[`${key}_chunk_${i}`] = chunk);
  storage[`${key}_meta`] = { chunks: chunks.length };
  await chrome.storage.local.set(storage);
}

IndexedDB as Overflow

For data exceeding local storage, use IndexedDB:

const DB_NAME = 'ExtensionDB';
const STORE_NAME = 'LargeData';

async function initDB() {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open(DB_NAME, 1);
    request.onupgradeneeded = (e) => {
      e.target.result.createObjectStore(STORE_NAME);
    };
    request.onsuccess = () => resolve(request.result);
    request.onerror = () => reject(request.error);
  });
}

Permission Considerations

Request unlimitedStorage in manifest to bypass quotas:

{
  "permissions": ["unlimitedStorage"]
}

Note: This still doesn’t guarantee infinite storage; browsers may still enforce limits.

Sync Storage Optimization

Minimize sync storage usage:

// Only sync critical small data
async function syncSettings(settings) {
  const critical = { theme: settings.theme, language: settings.language };
  await chrome.storage.sync.set(critical);
}

Batch Operations

Group writes to avoid write limits:

async function batchSet(items) {
  const storage = {};
  items.forEach(({ key, value }) => storage[key] = value);
  await chrome.storage.local.set(storage);
}

Cleaning Orphaned Data

async function cleanup() {
  const all = await chrome.storage.local.get();
  const now = Date.now();
  for (const [key, value] of Object.entries(all)) {
    if (value?.expires && value.expires < now) {
      await chrome.storage.local.remove(key);
    }
  }
}

User-Facing Storage UI

Display usage to users for transparency:

function renderStorageUI(container) {
  getStorageUsage().then(({ used, percent }) => {
    container.innerHTML = `
      <div class="storage-bar">
        <div style="width: ${percent}%"></div>
      </div>
      <p>${(used / 1024 / 1024).toFixed(2)} MB used</p>
    `;
  });
}

Part of the Chrome Extension Guide by theluckystrike. Built at zovo.one.