High-performance LocalStorage compression using native CompressionStream API.
Store up to 10x more data in LocalStorage with browser-native GZIP compression. Zero dependencies, under 1KB, non-blocking.
| Feature | Description |
|---|---|
| ๐ Native Speed | Uses browser's C++ compression engine, not JavaScript |
| ๐ฆ < 1KB | Minimal footprint, zero dependencies |
| โก Non-Blocking | Stream-based async API prevents UI freezing |
| ๐ง Smart Threshold | Auto-skips compression for small data |
| ๐ TypeScript | Full type definitions included |
| ๐ฏ Simple API | Just setItem and getItem |
| Metric | NanoStorage | lz-string | Winner |
|---|---|---|---|
| Compress Time | 95 ms | 1.3 s | ๐ NanoStorage (14x) |
| Decompress Time | 57 ms | 67 ms | ๐ NanoStorage |
| Compressed Size | 70 KB | 168 KB | ๐ NanoStorage (2.4x) |
| Compression Ratio | 98.6% | 96.6% | ๐ NanoStorage |
๐ก 4/4 categories won! 5 MB JSON โ 70 KB in 95ms with faster decompression.
| Feature | lz-string | NanoStorage |
|---|---|---|
| Engine | JavaScript (Main Thread) | C++ (Browser Native) |
| UI Blocking | โ Yes, freezes on big data | โ No, async streams |
| Bundle Size | ~18 KB | < 1 KB |
| Algorithm | LZW (1984) | GZIP/Deflate (Industry Standard) |
๐ Original: 1 MB JSON
โ GZIP: ~100 KB
โ Base64: ~133 KB
๐พ Final: 133 KB (87% savings!)
npm install @qantesm/nanostorageyarn add @qantesm/nanostoragepnpm add @qantesm/nanostorageimport { nanoStorage } from '@qantesm/nanostorage';
// Store data (automatically compressed)
await nanoStorage.setItem('user', {
name: 'Muhammet',
preferences: { theme: 'dark', language: 'tr' },
history: [...largeArray]
});
// Retrieve data (automatically decompressed)
const user = await nanoStorage.getItem('user');
console.log(user.name); // 'Muhammet'import { nanoStorage } from '@qantesm/nanostorage';A pre-configured instance ready to use.
import { createStorage } from '@qantesm/nanostorage';
const storage = createStorage({
threshold: 500, // Bytes. Skip compression for smaller data
algorithm: 'gzip', // 'gzip' or 'deflate'
keyPrefix: 'myapp:', // Prefix for all keys
});Store any JSON-serializable value with automatic compression.
await storage.setItem('settings', { theme: 'dark' });
await storage.setItem('items', [1, 2, 3, 4, 5]);
await storage.setItem('count', 42);Retrieve and decompress a stored value.
const settings = await storage.getItem<Settings>('settings');
if (settings) {
console.log(settings.theme);
}Remove an item from storage.
await storage.removeItem('settings');Check if a key exists.
if (await storage.hasItem('user')) {
// User data exists
}Get all stored keys.
const allKeys = await storage.keys();
// ['user', 'settings', 'cache']Remove all items managed by this instance.
await storage.clear();Get compression statistics.
const stats = await storage.getStats();
console.log(`Compression ratio: ${(1 - stats.compressionRatio) * 100}%`);
// "Compression ratio: 85%"For advanced use cases, you can use the compression functions directly:
import { compress, decompress, isSupported } from '@qantesm/nanostorage';
// Check browser support
if (!isSupported()) {
console.warn('CompressionStream not available');
}
// Direct compression
const result = await compress({ data: 'large payload' });
console.log(result.data); // Compressed string
console.log(result.originalSize); // Original byte size
console.log(result.compressedSize); // Compressed byte size
console.log(result.wasCompressed); // true if compression was applied
// Direct decompression
const original = await decompress(result.data);| Option | Type | Default | Description |
|---|---|---|---|
threshold |
number |
500 |
Minimum bytes to trigger compression. Smaller data is stored raw. |
algorithm |
'gzip' | 'deflate' |
'gzip' |
Compression algorithm to use. |
keyPrefix |
string |
'ns:' |
Prefix added to all storage keys. |
GZIP adds ~18 bytes of header overhead. For tiny data like { theme: 'dark' }, compression would actually increase size. The threshold ensures only beneficial compressions occur.
| Browser | Version | Status |
|---|---|---|
| Chrome | 80+ | โ Supported |
| Edge | 80+ | โ Supported |
| Firefox | 113+ | โ Supported |
| Safari | 16.4+ | โ Supported |
| Opera | 67+ | โ Supported |
| IE | All | โ Not Supported |
await nanoStorage.setItem('gameState', {
level: 42,
inventory: [...hundredsOfItems],
achievements: [...],
settings: {...}
});// Save draft as user types
await nanoStorage.setItem('formDraft', formData);
// Restore on page reload
const draft = await nanoStorage.getItem('formDraft');
if (draft) {
restoreForm(draft);
}await nanoStorage.setItem('cart', {
items: cartItems,
lastUpdated: Date.now()
});// Persist state
store.subscribe(() => {
nanoStorage.setItem('appState', store.getState());
});
// Hydrate on load
const savedState = await nanoStorage.getItem('appState');
if (savedState) {
store.dispatch({ type: 'HYDRATE', payload: savedState });
}Unlike native localStorage.getItem() which is synchronous, NanoStorage uses Promises:
// โ Won't work
const data = nanoStorage.getItem('key');
// โ
Correct
const data = await nanoStorage.getItem('key');This is intentional - async operations prevent UI blocking during compression.
// โ
These work
await storage.setItem('obj', { a: 1 });
await storage.setItem('arr', [1, 2, 3]);
await storage.setItem('str', 'hello');
await storage.setItem('num', 42);
await storage.setItem('bool', true);
await storage.setItem('null', null);
// โ These won't work
await storage.setItem('fn', () => {}); // Functions
await storage.setItem('date', new Date()); // Dates (use .toISOString())
await storage.setItem('map', new Map()); // Map/Set (convert to array)โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโ
โ JavaScript โ โโโบ โ TextEncoder โ โโโบ โ CompressionStreamโ โโโบ โ Base64 โ
โ Object โ โ (UTF-8) โ โ (Native GZIP) โ โ String โ
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโ
โโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
โ Base64 โ โโโบ โ DecompressionStreamโ โโโบ โ TextDecoder โ โโโบ โ JavaScript โ
โ String โ โ (Native GZIP) โ โ (UTF-8) โ โ Object โ
โโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
Compressed data is prefixed with a marker byte:
R- Raw (uncompressed) dataG- GZIP compressedD- Deflate compressed
MIT ยฉ Muhammet Ali Bรผyรผk
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing) - Open a Pull Request