Local Storage with Expiry
Enhanced localStorage with automatic expiry, supports TTL (Time-To-Live) for stored items
Implementation
Copy the complete example or adapt its typed interface to your project. Comments are kept inside the snippet so the intent travels with the code.
/**
* LocalStorage with expiry support
*/
const ExpiryStorage = {
/**
* Set item with expiry
* @param key Storage key
* @param value Value to store
* @param ttl Time-to-live in milliseconds (default: 24h)
*/
setItem<T>(key: string, value: T, ttl = 86400000): void {
const item = {
value,
expiry: Date.now() + ttl
};
localStorage.setItem(key, JSON.stringify(item));
},
/**
* Get item (returns null if expired)
* @param key Storage key
* @returns Stored value or null
*/
getItem<T>(key: string): T | null {
const itemStr = localStorage.getItem(key);
if (!itemStr) return null;
const item = JSON.parse(itemStr);
if (Date.now() > item.expiry) {
localStorage.removeItem(key);
return null;
}
return item.value as T;
},
/**
* Remove item
* @param key Storage key
*/
removeItem(key: string): void {
localStorage.removeItem(key);
},
/**
* Clear all items
*/
clear(): void {
localStorage.clear();
}
};
// Usage Example
ExpiryStorage.setItem('user', { id: 1, name: 'John' }, 3600000); // 1 hour TTL
const user = ExpiryStorage.getItem('user'); // { id: 1, name: 'John' } (if not expired)When to use it
Perfect for caching API responses, user sessions, or temporary data with automatic cleanup
Review the input assumptions and browser or runtime support before adopting a utility unchanged. Small helpers are easiest to maintain when their contract stays narrow.
Reasoning and trade-offs
How it works
The stored envelope pairs a value with an absolute expiry timestamp. Reads parse the envelope, compare the timestamp with the current clock, remove expired data, and otherwise return the original value.
What to watch for
Local storage is synchronous, limited, user-controlled, and unavailable in some privacy or server-rendered contexts. Parsing can fail, clocks can change, quota writes can throw, and stored data must never be treated as secure.
Production use
Use expiring storage for disposable preferences or cache hints that can be recomputed. Avoid credentials and sensitive personal data, guard all browser access, and version the envelope when its shape may evolve.
Before you ship
- Confirm the input contract with tests that cover empty, invalid, boundary, and representative real-world values before sharing the helper across a codebase.
- Keep the function focused on one responsibility and name any project-specific policy explicitly instead of hiding it inside a generic-looking utility.
- Measure behavior in the browsers or runtime versions your product supports, especially when timing, storage, DOM state, locale, or network access is involved.
- Document the return type, mutation behavior, failure mode, and performance characteristics beside the call site so future maintainers can evaluate changes safely.
- Compare the example with current official platform documentation when it relies on a browser API, language feature, or runtime behavior that may change over time.
- If the helper crosses a trust boundary, validate again on the server and avoid treating browser-side checks, TypeScript annotations, or formatted output as a security guarantee.
- Add a small regression test whenever a real edge case is discovered; concrete examples preserve the reason behind a guard more reliably than a general comment.