Reading and writing values
Store rich JavaScript values, add TypeScript types, and initialize or update entries.
import { createStorage } from 'ultrastorage';
const storage = createStorage();
Rich values
Yes, localStorage can finally handle a Set, or any data type you throw at it. It only took the entire JavaScript ecosystem to get here.
Uses devalue by default, but you can bring your own serializer.
storage.setItem('tags', new Set(['a', 'b', 'c']));
storage.getItem('tags'); // Set {'a', 'b', 'c'}
storage.setItem('metadata', new Map([['key', 'value']]));
storage.getItem('metadata'); // Map {'key' => 'value'}
storage.setItem('date', new Date('2025-01-01'));
storage.getItem('date'); // Date 2025-01-01T00:00:00.000Z
TypeScript types
TypeScript can’t read localStorage at compile time (yet), but you can at least pretend your data is typed.
interface User {
name: string;
age: number;
}
const user = storage.getItem<User>('user');
// user is typed as User | null
storage.getOrInit<User>('user', () => ({ name: 'Alice', age: 30 }));
storage.updateItem<User>('user', (current) => ({
...current!,
age: current!.age + 1,
}));
However, the true safe way is to validate with a schema during read.
Initialize a value
Get the value if it exists, or writes to storage if it doesn’t. Either way, you’re getting something back.
const prefs = storage.getOrInit('prefs', () => ({
theme: 'light',
lang: 'en',
}));
Update a value
Read-modify-write in one call. Three separate statements was apparently too much work even when AI is writing all the code.
storage.updateItem<number>('count', (current) => (current ?? 0) + 1);
Check, remove, and clear
The usual housekeeping. Someone has to take out the trash.
storage.has('user'); // true
storage.removeItem('user');
storage.has('user'); // false
storage.clear(); // remove all entries written by `ultrastorage`
storage.clearExpired(); // remove only expired entries