localspace β modern storage toolkit that keeps localForage compatibility while using async/await, TypeScript, and zero legacy baggage.
The industry still leans on localForage's familiar API, yet modern apps crave stronger typing, async ergonomics, and multi-platform reliability without a painful rewrite. localspace exists to bridge that gap: it honors the old contract while delivering first-class TypeScript types, native async/await, reliable IndexedDB cleanup, and a clean driver architecture.
Why rebuild instead of fork? Starting fresh let us eliminate technical debt while maintaining API compatibility. Teams can migrate from localForage without changing application code, then unlock better developer experience and future extensibility.
Get started in 5 minutes:
npm install localspace
# or: yarn add localspace / pnpm add localspaceimport localspace from 'localspace';
// Store and retrieve data
await localspace.setItem('user', { name: 'Ada', role: 'admin' });
const user = await localspace.getItem<{ name: string; role: string }>('user');
// TypeScript generics for type safety
interface User {
name: string;
role: string;
}
const typedUser = await localspace.getItem<User>('user');const cache = localspace.createInstance({
name: 'my-app',
storeName: 'cache',
});
await cache.setItem('token', 'abc123');// Write multiple items in one transaction (IndexedDB)
await localspace.setItems([
{ key: 'user:1', value: { name: 'Ada' } },
{ key: 'user:2', value: { name: 'Grace' } },
]);
// Read multiple items
const users = await localspace.getItems(['user:1', 'user:2']);import localspace, { ttlPlugin, encryptionPlugin } from 'localspace';
const secureStore = localspace.createInstance({
name: 'secure',
plugins: [
ttlPlugin({ defaultTTL: 60_000 }), // Auto-expire after 1 minute
encryptionPlugin({ key: 'your-32-byte-key' }), // Encrypt data
],
});That's it! For more details, see the sections below.
- Installation
- Core API
- Batch Operations
- Coalesced Writes
- Plugin System
- Configuration
- Performance Notes
- Troubleshooting
- Documentation
- License
npm install localspace
# or
yarn add localspace
# or
pnpm add localspaceimport localspace from 'localspace';Bundles included: ES modules, CommonJS, UMD, plus .d.ts files.
// Set and get items
await localspace.setItem('key', value);
const value = await localspace.getItem<T>('key');
// Remove items
await localspace.removeItem('key');
await localspace.clear();
// Query
const count = await localspace.length();
const keys = await localspace.keys();
// Iterate
await localspace.iterate<T, void>((value, key, index) => {
console.log(key, value);
});localspace.getItem('user', (error, value) => {
if (error) return console.error(error);
console.log(value);
});// Prefer IndexedDB with localStorage fallback
await localspace.setDriver([localspace.INDEXEDDB, localspace.LOCALSTORAGE]);
// Check current driver
console.log(localspace.driver()); // 'asyncStorage' or 'localStorageWrapper'π Full API Reference: docs/api-reference.md
Use batch APIs for better performance with IndexedDB:
// Single transaction write
await localspace.setItems([
{ key: 'user:1', value: { name: 'Ada' } },
{ key: 'user:2', value: { name: 'Lin' } },
]);
// Ordered bulk read
const result = await localspace.getItems(['user:1', 'user:2']);
// [{ key: 'user:1', value: {...} }, { key: 'user:2', value: {...} }]
// Single transaction delete
await localspace.removeItems(['user:1', 'user:2']);For atomic multi-step operations:
await localspace.runTransaction('readwrite', async (tx) => {
const current = (await tx.get<number>('counter')) ?? 0;
await tx.set('counter', current + 1);
await tx.set('lastUpdated', Date.now());
});Opt-in automatic batching of rapid writes for 3-10x performance improvement:
const store = localspace.createInstance({
coalesceWrites: true, // Enable (default: false)
coalesceWindowMs: 8, // 8ms merge window
});
// These are automatically batched into one transaction
await Promise.all([
store.setItem('a', 1),
store.setItem('b', 2),
store.setItem('c', 3),
]);Consistency modes:
'strong'(default): Reads flush pending writes first'eventual': Reads may see stale values briefly
// Get performance stats
const stats = localspace.getPerformanceStats?.();
// { totalWrites: 150, coalescedWrites: 120, transactionsSaved: 100 }localspace ships with a powerful plugin engine:
import localspace, {
ttlPlugin,
compressionPlugin,
encryptionPlugin,
syncPlugin,
quotaPlugin,
} from 'localspace';
const store = localspace.createInstance({
name: 'secure-store',
plugins: [
ttlPlugin({ defaultTTL: 60_000 }), // Auto-expire
compressionPlugin({ threshold: 1024 }), // Compress > 1KB
encryptionPlugin({ key: '32-byte-key-here' }), // Encrypt
syncPlugin({ channelName: 'my-app' }), // Multi-tab sync
quotaPlugin({ maxSize: 5 * 1024 * 1024 }), // 5MB limit
],
pluginErrorPolicy: 'strict', // Recommended for encryption
});| Plugin | Purpose |
|---|---|
| TTL | Auto-expire items with { data, expiresAt } wrapper |
| Encryption | AES-GCM encryption via Web Crypto API |
| Compression | LZ-string compression for large values |
| Sync | Multi-tab synchronization via BroadcastChannel |
| Quota | Storage limit enforcement with LRU eviction |
π Full Plugin Documentation: docs/plugins.md π Real-World Examples: docs/examples.md
const store = localspace.createInstance({
// Database
name: 'myapp', // Database name
storeName: 'data', // Store name
version: 1, // Schema version
// Driver
driver: [localspace.INDEXEDDB, localspace.LOCALSTORAGE],
// IndexedDB performance
durability: 'relaxed', // 'relaxed' (fast) or 'strict'
prewarmTransactions: true, // Pre-warm connection
// Batching
maxBatchSize: 200, // Split large batches
coalesceWrites: false, // Merge rapid writes
// Plugins
plugins: [],
pluginErrorPolicy: 'lenient', // 'strict' for encryption
});π Full Configuration Options: docs/api-reference.md#configuration-options
- Batch APIs outperform loops:
setItems()~6x faster,getItems()~7.7x faster than per-item loops - Coalesced writes: 3-10x faster under write bursts (opt-in)
- Transaction helpers:
runTransaction()for atomic migrations - IndexedDB durability: Chrome 121+ uses relaxed durability by default
- localStorage batches are non-atomic: Prefer IndexedDB for atomic operations
| Issue | Solution |
|---|---|
| Driver not ready | Call await localspace.ready() before first operation |
| Quota errors | Check error.code === 'QUOTA_EXCEEDED' |
| Plugin errors swallowed | Set pluginErrorPolicy: 'strict' |
| Stale reads with coalescing | Use coalesceReadConsistency: 'strong' (default) |
Errors are LocalSpaceError with code, details, and cause properties.
- Browsers: Modern Chromium/Edge, Firefox, Safari
- Drivers: IndexedDB (primary), localStorage (fallback)
- WebSQL: Not supported (migrate to IndexedDB)
- Node/SSR: Custom driver required
| Document | Description |
|---|---|
| API Reference | Complete method documentation |
| Plugin System | Built-in plugins & custom development |
| Real-World Examples | Production-ready code patterns |
| Migration Guide | Upgrading from localForage |
- IndexedDB and localStorage drivers
- Full localForage API parity
- TypeScript-first implementation
- Batch operations & write coalescing
- Plugin system (TTL, Encryption, Compression, Sync, Quota)
- OPFS driver (Origin Private File System)
- Node.js (File system, SQLite)
- React Native (AsyncStorage, SQLite)
- Deno (Native KV store)