Skip to main content

Web Storage extension

The Web Storage extension (ext/webstorage/) implements the Web Storage specification in Deno, providing localStorage and sessionStorage APIs for client-side data persistence.

Overview

The Web Storage API provides two mechanisms for storing key-value pairs in a web-compatible way:
  • localStorage - Persistent storage that survives browser restarts
  • sessionStorage - Temporary storage cleared when the session ends
In Deno, both localStorage and sessionStorage are available when running in a browser-like context or when explicitly enabled.

Basic usage

Storing data

// Store a value
localStorage.setItem("username", "alice");
localStorage.setItem("theme", "dark");

// Store numbers (converted to strings)
localStorage.setItem("count", "42");

// Store objects (must be serialized)
const user = { id: 1, name: "Alice" };
localStorage.setItem("user", JSON.stringify(user));

Retrieving data

// Get a value
const username = localStorage.getItem("username");
console.log(username); // "alice"

// Get a number
const count = parseInt(localStorage.getItem("count") || "0");

// Get an object
const userJson = localStorage.getItem("user");
const user = userJson ? JSON.parse(userJson) : null;

Removing data

// Remove a specific item
localStorage.removeItem("theme");

// Clear all items
localStorage.clear();

Checking for existence

// Check if a key exists
if (localStorage.getItem("username") !== null) {
  console.log("Username is set");
}

// Get number of stored items
console.log("Items stored:", localStorage.length);

Working with complex data

Storing arrays

const items = ["apple", "banana", "orange"];
localStorage.setItem("fruits", JSON.stringify(items));

// Retrieve
const fruitsJson = localStorage.getItem("fruits");
const fruits = fruitsJson ? JSON.parse(fruitsJson) : [];
console.log(fruits); // ["apple", "banana", "orange"]

Storing objects with metadata

interface StoredData<T> {
  value: T;
  timestamp: number;
  expiresAt?: number;
}

function setWithExpiry<T>(key: string, value: T, ttlMs?: number) {
  const data: StoredData<T> = {
    value,
    timestamp: Date.now(),
    expiresAt: ttlMs ? Date.now() + ttlMs : undefined,
  };
  localStorage.setItem(key, JSON.stringify(data));
}

function getWithExpiry<T>(key: string): T | null {
  const json = localStorage.getItem(key);
  if (!json) return null;
  
  const data: StoredData<T> = JSON.parse(json);
  
  // Check if expired
  if (data.expiresAt && Date.now() > data.expiresAt) {
    localStorage.removeItem(key);
    return null;
  }
  
  return data.value;
}

// Usage
setWithExpiry("session", { userId: 123 }, 3600000); // 1 hour TTL
const session = getWithExpiry<{ userId: number }>("session");

sessionStorage

sessionStorage works identically to localStorage but has different persistence characteristics:
// Store temporary data
sessionStorage.setItem("tempToken", "abc123");

// Retrieve
const token = sessionStorage.getItem("tempToken");

// Clear session data
sessionStorage.clear();

Differences from localStorage

FeaturelocalStoragesessionStorage
PersistenceSurvives restartsCleared on session end
ScopeAll tabs/windowsSingle tab/window
Use caseLong-term storageTemporary session data

Iterating over storage

Get all keys

function getAllKeys(): string[] {
  const keys: string[] = [];
  for (let i = 0; i < localStorage.length; i++) {
    const key = localStorage.key(i);
    if (key) keys.push(key);
  }
  return keys;
}

const keys = getAllKeys();
console.log("Stored keys:", keys);

Get all items

function getAllItems(): Record<string, string> {
  const items: Record<string, string> = {};
  for (let i = 0; i < localStorage.length; i++) {
    const key = localStorage.key(i);
    if (key) {
      const value = localStorage.getItem(key);
      if (value !== null) {
        items[key] = value;
      }
    }
  }
  return items;
}

const allData = getAllItems();
console.log("All stored data:", allData);

Storage events

Listen for changes to storage (works across different browsing contexts):
window.addEventListener("storage", (event: StorageEvent) => {
  console.log("Storage changed:");
  console.log("Key:", event.key);
  console.log("Old value:", event.oldValue);
  console.log("New value:", event.newValue);
  console.log("URL:", event.url);
  console.log("Storage area:", event.storageArea);
});
Storage events are fired when storage is modified in a different window/tab of the same origin, not in the current context.

API reference

Storage interface

Both localStorage and sessionStorage implement the Storage interface:
length
number
Returns the number of key-value pairs stored.
key
(index: number) => string | null
Returns the name of the key at the specified index, or null if the index is out of range.
getItem
(key: string) => string | null
Returns the value associated with the given key, or null if the key doesn’t exist.
setItem
(key: string, value: string) => void
Stores a key-value pair. If the key already exists, its value is updated.
removeItem
(key: string) => void
Removes the key-value pair with the given key.
clear
() => void
Removes all key-value pairs.

Storage limits

Web Storage implementations typically have size limits (often 5-10 MB per origin). Exceeding the quota will throw a QuotaExceededError.
try {
  localStorage.setItem("largeData", "x".repeat(10_000_000));
} catch (error) {
  if (error instanceof DOMException && error.name === "QuotaExceededError") {
    console.error("Storage quota exceeded");
  }
}

Best practices

getItem() returns null if the key doesn’t exist:
const value = localStorage.getItem("key") ?? "default";
Web Storage only stores strings, so serialize objects:
// Store
localStorage.setItem("data", JSON.stringify({ x: 1 }));

// Retrieve
const data = JSON.parse(localStorage.getItem("data") || "null");
Always wrap JSON.parse() in try-catch:
try {
  const data = JSON.parse(localStorage.getItem("data") || "null");
} catch {
  console.error("Invalid JSON in storage");
}
Prefix keys to avoid conflicts:
localStorage.setItem("myapp:user:preferences", data);
Web Storage is not encrypted. Never store passwords, tokens, or sensitive information.

Security considerations

  • Not secure: Data is stored in plain text
  • XSS vulnerable: Accessible via JavaScript
  • Same-origin policy: Only accessible within the same origin
  • Use cookies for sensitive data: Prefer secure, httpOnly cookies for authentication tokens

Build docs developers (and LLMs) love