### Create storage instance with prefix Source: https://ultrastorage.dev/guides/validation Creates a storage instance with a prefix. This is the setup required for the validation examples. ```ts import { createStorage } from 'ultrastorage'; export const appStorage = createStorage({ prefix: 'acme' }); ``` -------------------------------- ### Install ultrastorage with npm Source: https://ultrastorage.dev/getting-started Installs the ultrastorage package, which includes both the storage API and the optional React adapter. ```sh npm install ultrastorage ``` -------------------------------- ### getOrInit() migration example Source: https://ultrastorage.dev/reference/storage Demonstrates using getOrInit to migrate data from standard localStorage. ```ts const theme = appStorage.getOrInit('theme', () => localStorage.getItem('theme')); ``` -------------------------------- ### getOrInit() initialization example Source: https://ultrastorage.dev/reference/storage Demonstrates initializing a value when a read returns null. ```ts const preferences = appStorage.getOrInit('preferences', () => ({ theme: 'system', locale: 'en', })); ``` -------------------------------- ### removeItem() usage example Source: https://ultrastorage.dev/reference/storage Demonstrates removing a key and verifying its removal. ```ts appStorage.removeItem('draft'); appStorage.getItem('draft'); // null ``` -------------------------------- ### has() usage example Source: https://ultrastorage.dev/reference/storage Demonstrates using has() to conditionally set a value. ```ts if (!appStorage.has('preferences')) { appStorage.setItem('preferences', { theme: 'system' }); } ``` -------------------------------- ### setItem() usage example Source: https://ultrastorage.dev/reference/storage Stores a rich object with a time-to-live (TTL) of 24 hours. ```ts appStorage.setItem( 'draft', { title: 'Introducing ultrastorage', updatedAt: new Date(), }, { ttl: 86_400_000 }, ); ``` -------------------------------- ### subscribe() signature and usage Source: https://ultrastorage.dev/reference/storage Signature and usage example for subscribe(). SubscribeOptions has optional reactiveExpiration (default false). Returns an idempotent unsubscribe function that also cancels its timer. See the subscription guide for event types, timing, errors, and cross-tab behavior. ```ts appStorage.subscribe( key: StorageKey, listener: StorageListener, options?: SubscribeOptions, ): () => void; ``` ```ts const unsubscribe = appStorage.subscribe('theme', (change) => { const theme = appStorage.getItem<'light' | 'dark'>('theme'); console.log(change.type, theme); }); appStorage.setItem('theme', 'dark'); // Logs: set dark unsubscribe(); ``` -------------------------------- ### Custom separator example: separator '/' Source: https://ultrastorage.dev/guides/namespaces Shows how to use a custom separator to match an existing stored-key convention. The separator is placed between the prefix and the key. ```TypeScript import { createStorage } from 'ultrastorage'; export const appStorage = createStorage({ prefix: 'acme', separator: '/' }); ``` ```TypeScript import { appStorage } from './lib/app-storage'; appStorage.setItem('theme', 'dark'); // stored key: acme/theme ``` -------------------------------- ### key() signature and usage Source: https://ultrastorage.dev/reference/storage Signature and usage example for key(). Returns the key at the given zero-based index among non-expired entries, or null if out of bounds. Expired entries are skipped but not removed. Repeated calls to enumerate every key perform quadratic work; use keys() for full enumeration. ```ts appStorage.key(index: number): StorageKey | null; ``` ```ts appStorage.setItem('theme', 'dark'); appStorage.setItem(['users', '42'], 'Alice'); appStorage.key(0); // 'theme' appStorage.key(1); // ['users', '42'] appStorage.key(2); // null ``` -------------------------------- ### Basic appStorage setItem and getItem Source: https://ultrastorage.dev/reference/storage Shows the basic usage of the appStorage instance: setting and getting a simple string value. The getItem call returns the stored value 'dark'. ```ts import { appStorage } from './lib/app-storage'; appStorage.setItem('theme', 'dark'); appStorage.getItem('theme'); // 'dark' ``` -------------------------------- ### getItemResult() usage example Source: https://ultrastorage.dev/reference/storage Demonstrates handling different read statuses such as success, missing, or validation errors. ```ts const result = appStorage.getItemResult('theme'); if (result.status === 'success') { console.log(result.value); // A stored null is a successful read. } else if (result.status === 'missing') { appStorage.setItem('theme', 'light'); } else if (result.status === 'validation-error') { console.error(result.issues); } ``` -------------------------------- ### keys() example with array keys Source: https://ultrastorage.dev/reference/storage Shows that keys are returned relative to the prefix, with array keys restored as fresh arrays containing their original segments. The result is a snapshot: later writes or changes to the returned array, including nested key arrays, do not affect each other. ```ts appStorage.setItem('theme', 'dark'); appStorage.setItem(['users', '42'], 'Alice'); appStorage.keys(); // ['theme', ['users', '42']] ``` -------------------------------- ### clear() usage example Source: https://ultrastorage.dev/reference/storage Demonstrates that clear() only affects the current namespace and leaves other localStorage entries untouched. ```ts appStorage.setItem('theme', 'dark'); localStorage.setItem('analytics-id', 'abc123'); appStorage.clear(); appStorage.getItem('theme'); // null localStorage.getItem('analytics-id'); // 'abc123' ``` -------------------------------- ### Store and retrieve values with array keys Source: https://ultrastorage.dev/guides/values Use an array of strings as the key when several values belong to the same entity or scope. The example stores and retrieves profile and preferences for a user ID, showing that array keys group related entries. ```ts const userId = '42'; appStorage.setItem(['users', userId, 'profile'], { name: 'Alice', }); appStorage.setItem(['users', userId, 'preferences'], { theme: 'dark', }); appStorage.getItem(['users', userId, 'profile']); // { name: 'Alice' } appStorage.getItem(['users', userId, 'preferences']); // { theme: 'dark' } ``` -------------------------------- ### useAppStorage with Zod schema Source: https://ultrastorage.dev/guides/react Example using useAppStorage with a Zod schema to validate the stored user data at runtime. Schemas provide both compile-time types and runtime validation, and determine the value and setter types. Note that writes are not validated, so the schema must accept the new values on the next read. ```tsx import { z } from 'zod'; import { useAppStorage } from './lib/app-storage'; const UserSchema = z.object({ name: z.string(), age: z.number() }); export function Profile() { const [user, setUser, removeUser] = useAppStorage('user', { schema: UserSchema }); // user: { name: string; age: number } | null return ( <>

{user?.name ?? 'No profile saved'}

); } ``` -------------------------------- ### updateItem() signature and usage Source: https://ultrastorage.dev/reference/storage Signature and usage example for updateItem(). The updater must be synchronous and receives the value without schema validation. Updates are not atomic across tabs or concurrent callers. ttl and expiresAt cannot be used together, including expiresAt: null. ```ts appStorage.updateItem( key: StorageKey, updater: (value: T | null) => T, options?: StorageOptions, ): T; ``` ```ts const count = appStorage.updateItem('cart-count', (current) => (current ?? 0) + 1); // 1 when the key was previously missing ``` -------------------------------- ### Update with expiration options Source: https://ultrastorage.dev/guides/values Shows how expiration is handled during updates. Omitting TTL preserves the original deadline; passing a TTL starts a new lifetime from the update. To remove expiration during an update, pass { expiresAt: null }. ```ts appStorage.setItem('count', 1, { ttl: 60_000 }); appStorage.updateItem('count', (current) => (current ?? 0) + 1); // 'count' keeps its original expiration deadline. appStorage.updateItem('count', (current) => (current ?? 0) + 1, { ttl: 60_000 }); // 'count' now expires 60 seconds after this update. ``` -------------------------------- ### Enable reactive expiration with useAppStorage Source: https://ultrastorage.dev/guides/react Enables reactive expiration for a useAppStorage hook value. When the entry expires, the hook value updates to its default ('Ready') or null. Timers are canceled on unmount, key or storage changes, or disabling the option; SSR and rendering never start timers. ```tsx const [notice] = useAppStorage('notice', { defaultValue: 'Ready', reactiveExpiration: true, }); ``` -------------------------------- ### Create a storage instance and write/subscribe with ultrastorage Source: https://ultrastorage.dev/ Create a shared storage instance with a prefix, then write a rich object with a TTL and subscribe to changes for a specific key. Use this snippet to set up a storage instance and demonstrate writing and subscribing in one workflow. ```javascript import { createStorage } from 'ultrastorage'; const storage = createStorage({ prefix: 'editor' }); storage.setItem( 'draft:post-42', { title: 'Introducing ultrastorage', updatedAt: new Date(), tags: new Set(['release', 'docs']), }, { ttl: 86_400_000 }, ); storage.subscribe('draft:post-42', refreshPreview); ``` -------------------------------- ### Set, get, and remove a value with appStorage Source: https://ultrastorage.dev/getting-started Uses the shared instance to set, get, and remove a value. The getItem call returns the deserialized value. ```ts import { appStorage } from './lib/app-storage'; appStorage.setItem('theme', 'dark'); appStorage.getItem('theme'); // 'dark' appStorage.removeItem('theme'); ``` -------------------------------- ### Create storage with custom serializer from ultrastorage/core Source: https://ultrastorage.dev/guides/destinations Use this snippet to avoid bundling `devalue` by importing from `ultrastorage/core` instead of `ultrastorage`. The only difference is that `serializer` is required when using the core entry point. ```ts import { createStorage } from 'ultrastorage/core'; import superjson from 'superjson'; export const appStorage = createStorage({ prefix: 'acme', serializer: { stringify: superjson.stringify, parse: superjson.parse }, }); ``` -------------------------------- ### getOrInit() Source: https://ultrastorage.dev/reference/storage Returns the existing value for a key, or creates and stores a new value using a factory function. ```APIDOC ## getOrInit() ### Description Returns the existing value for `key`, or calls `factory()` to create, store, and return a new value. Useful for initializing values or migrating from existing `localStorage` keys. ### Method getOrInit ### Signature ```ts appStorage.getOrInit( key: StorageKey, factory: () => T, options?: StorageOptions, ): T; ``` ### Parameters - **key** (StorageKey) - Required - The key to retrieve or initialize. - **factory** (() => T) - Required - Synchronous function that returns the value to store if the key is missing, expired, unreadable, or stored-null. - **options** (StorageOptions) - Optional - Expiration options: - `ttl` (number) - Optional - Finite time-to-live in milliseconds. - `expiresAt` (Date | number | null) - Optional - Absolute deadline; null removes expiration. ### Returns - **T** - The existing value or the newly created value. ### Behavior - `ttl` and `expiresAt` cannot be used together, including `expiresAt: null`. - Omitting expiration options preserves the current unexpired deadline. - Missing, expired, unreadable, and non-expiring entries are written without expiration. - Factories must be synchronous; initialization is not atomic across tabs or concurrent callers. ### Example ```ts const preferences = appStorage.getOrInit('preferences', () => ({ theme: 'system', locale: 'en', })); ``` ### Migration Example ```ts const theme = appStorage.getOrInit('theme', () => localStorage.getItem('theme')); ``` ``` -------------------------------- ### Using createMemoryStorage with createStorage Source: https://ultrastorage.dev/reference/storage Creates a storage instance with an in-memory backend and a 'test' prefix. Sets and retrieves a theme value, demonstrating the API usage. ```ts import { createMemoryStorage, createStorage } from 'ultrastorage'; const testStorage = createStorage({ prefix: 'test', storage: createMemoryStorage(), }); testStorage.setItem('theme', 'dark'); testStorage.getItem('theme'); // 'dark' ``` -------------------------------- ### createStorage() Source: https://ultrastorage.dev/reference/storage Creates a new storage instance with optional configuration options. Returns an UltraStorage instance that provides familiar Storage methods with typed reads, rich-value serialization, expiration, namespacing, and subscriptions. ```APIDOC ## createStorage() ### Description Creates a new storage instance with optional configuration options. Returns an UltraStorage instance that provides familiar Storage methods with typed reads, rich-value serialization, expiration, namespacing, and subscriptions. ### Method createStorage ### Signature createStorage(options?: CreateStorageOptions): UltraStorage; ### Parameters #### Options (all optional) - **prefix** (string) - Optional - Key prefix for namespacing - **separator** (string) - Optional - Default: `":"` - Advanced override for the separator between prefix and key - **storage** (Storage) - Optional - Default: `localStorage` - Where values are saved - **serializer** (Serializer) - Optional - Default: `devalue` - Custom serializer with `stringify` and `parse` methods ### Notes - Keep the default `:` separator unless you need to match an existing stored-key convention. - Changing `prefix` or `separator` after writing data requires explicit migration. - Also available from `ultrastorage/core` where `serializer` is **required** and `devalue` is not bundled. - `getItem()` returns deserialized values rather than always returning strings. - Named property access such as `storage.theme` is unsupported; use `getItem('theme')` and `setItem('theme', value)`. - Existing raw storage values require explicit migration, and `clear()` only removes recognized entries within the configured namespace. ### Example ```ts import { createStorage } from 'ultrastorage'; export const appStorage = createStorage({ prefix: 'acme' }); ``` ``` -------------------------------- ### getOrInit() method signature Source: https://ultrastorage.dev/reference/storage Defines the method to return an existing value or initialize it using a factory function. ```ts appStorage.getOrInit( key: StorageKey, factory: () => T, options?: StorageOptions, ): T; ``` -------------------------------- ### clearExpired() signature and usage Source: https://ultrastorage.dev/reference/storage Signature and usage example for clearExpired(). Removes only expired entries in the current namespace. Unreadable entries are skipped, as with clear(). ```ts appStorage.clearExpired(): void; ``` ```ts appStorage.setItem('search-results', ['first', 'second'], { ttl: 5 * 60_000 }); // Later, such as when the application starts again: appStorage.clearExpired(); ``` -------------------------------- ### Remove expiration with expiresAt: null Source: https://ultrastorage.dev/guides/expiration Pass ttl or expiresAt to replace the deadline, or expiresAt: null to remove it. This example removes expiration from the entry. ```ts appStorage.setItem('draft', { title: 'Keep this draft' }, { expiresAt: null }); ``` -------------------------------- ### ThemePicker Client Component using useAppStorage Source: https://ultrastorage.dev/guides/react A Client Component ('use client') that reads and writes the 'theme' key via useAppStorage with defaultValue 'light'. Buttons toggle the current theme and remove the key. Use this in an RSC framework such as Next.js. Same-key components respond to these writes without any provider. ```tsx // use a Client Component in an RSC framework 'use client'; import { useAppStorage } from './lib/app-storage'; export function ThemePicker() { const [theme, setTheme, removeTheme] = useAppStorage('theme', { defaultValue: 'light', }); return ( <> ); } ``` -------------------------------- ### Overlapping prefixes example: acme:admin Source: https://ultrastorage.dev/guides/namespaces Shows how overlapping prefixes can cause data to be shared. Clearing the broader namespace removes entries from both. ```TypeScript import { createStorage } from 'ultrastorage'; const adminStorage = createStorage({ prefix: 'acme:admin' }); adminStorage.setItem('theme', 'dark'); // stored as "acme:admin:theme" appStorage.keys(); // ['admin:theme'] appStorage.clear(); adminStorage.getItem('theme'); // null ``` -------------------------------- ### Create shared app storage instance Source: https://ultrastorage.dev/guides/subscriptions Creates a shared ultrastorage instance with the 'app' prefix. Other instances using the same Storage object and fully prefixed key notify the same listeners. ```ts import { createStorage } from 'ultrastorage'; export const appStorage = createStorage({ prefix: 'app' }); ``` -------------------------------- ### Notify subscriber from another instance Source: https://ultrastorage.dev/guides/subscriptions Creates a separate instance with the same prefix and sets the 'theme' key, notifying the subscriber synchronously in the same page. ```ts import { createStorage } from 'ultrastorage'; const appStorage = createStorage({ prefix: 'app' }); appStorage.setItem('theme', 'dark'); // Notifies this page synchronously. ``` -------------------------------- ### Use equivalent array keys without reusing the same array instance Source: https://ultrastorage.dev/guides/values Equivalent arrays address the same entry, so you do not need to reuse the same array instance. This example shows that two separate array literals with the same segments access the same stored value. ```ts appStorage.setItem(['documents', 'draft'], 'Hello'); appStorage.getItem(['documents', 'draft']); // 'Hello' ``` -------------------------------- ### Type stored values with a type argument Source: https://ultrastorage.dev/guides/values Pass a type argument to describe the expected shape of a stored value. The example shows getItem, getOrInit, and updateItem with a User type, and notes that type arguments do not validate stored data at runtime. ```ts interface User { name: string; age: number; } const user = appStorage.getItem('user'); // user is typed as User | null appStorage.getOrInit('user', () => ({ name: 'Alice', age: 30 })); appStorage.updateItem('user', (current) => ({ ...current!, age: current!.age + 1, })); ``` -------------------------------- ### createStorage() function signature Source: https://ultrastorage.dev/reference/storage Creates a new storage instance. All options are optional. Returns an UltraStorage instance. ```ts createStorage(options?: CreateStorageOptions): UltraStorage; ``` -------------------------------- ### Update a value with updateItem() Source: https://ultrastorage.dev/guides/values Use to read, modify, and write a value in one call. Every update is a new write. Omitting expiration options preserves the current unexpired deadline; passing a TTL starts a new lifetime from the update. ```ts appStorage.updateItem('count', (current) => (current ?? 0) + 1); ``` -------------------------------- ### subscribe() Source: https://ultrastorage.dev/reference/storage Registers a listener to observe changes to a specific key, such as set, remove, or expire events. ```APIDOC ## subscribe(key, listener, options) ### Description Observes a specific key for changes. Returns an idempotent unsubscribe function. ### Parameters - **key** (StorageKey) - Required - The key to observe. - **listener** (StorageListener) - Required - Callback function triggered on events. - **options** (SubscribeOptions) - Optional - Configuration including `reactiveExpiration` (boolean). ### Example ```ts const unsubscribe = appStorage.subscribe('theme', (change) => { console.log(change.type); }); ``` ``` -------------------------------- ### length property signature and usage Source: https://ultrastorage.dev/reference/storage Signature and usage example for the length property. Expired entries are excluded from the count but not removed unless read via getItem() or swept with clearExpired(). Each access scans and decodes entries; use keys() for full enumeration. ```ts appStorage.length: number; ``` ```ts appStorage.setItem('theme', 'dark'); appStorage.setItem('locale', 'en'); console.log(appStorage.length); // 2 ``` -------------------------------- ### Initialize a value with getOrInit() Source: https://ultrastorage.dev/guides/values Use when a value may not exist yet and you want to create it on first access. The factory runs whenever the ordinary read returns null, including stored null, expired entries, and unreadable data. If the factory returns null, it will run again on the next call. ```ts const prefs = appStorage.getOrInit('prefs', () => ({ theme: 'light', lang: 'en', })); ``` -------------------------------- ### Preserve segment boundaries with array keys Source: https://ultrastorage.dev/guides/values Array keys preserve segment boundaries, so separator characters inside a segment do not change the key's structure. This example shows that 'a:b' as one segment is distinct from 'users:a' and 'b' as separate segments. ```ts appStorage.setItem(['users', 'a:b'], 'first'); appStorage.setItem(['users:a', 'b'], 'second'); appStorage.getItem(['users', 'a:b']); // 'first' appStorage.getItem(['users:a', 'b']); // 'second' ``` -------------------------------- ### Import legacy value with getItem legacy option Source: https://ultrastorage.dev/guides/migration Use the legacy option in getItem() to adopt a value previously written directly to the same storage backend. The destination uses the instance's namespace; legacy.key is an exact backend key without an added prefix. The required deserialize callback decodes the raw value. ```ts import { createStorage } from 'ultrastorage'; const appStorage = createStorage({ prefix: 'app' }); // Reads "app:theme" first. If missing, imports the raw "theme" value. const theme = appStorage.getItem('theme', { legacy: { key: 'theme', deserialize: (raw) => raw, }, }); ``` -------------------------------- ### Migrate ultrastorage entries with migrateStorage Source: https://ultrastorage.dev/guides/migration Migration loop that enumerates keys from ultrastorage, skips failed reads, and writes each value with its expiration to a destination. Use this as a template when moving away from ultrastorage; the destination API will differ. ```ts import type { StorageKey } from 'ultrastorage'; import { appStorage } from './lib/app-storage'; type Destination = { set(key: StorageKey, value: unknown, options: { expiresAt: number | null }): void | Promise; }; export async function migrateStorage(destination: Destination) { for (const key of appStorage.keys()) { const result = appStorage.getItemResult(key); if (result.status !== 'success') continue; const expiration = appStorage.getExpiration(key); await destination.set(key, result.value, { expiresAt: expiration?.expiresAt ?? null, }); } } ``` -------------------------------- ### Create storage with custom serializer (superjson) Source: https://ultrastorage.dev/guides/destinations Use this snippet to configure a custom serializer (here, superjson) via the `serializer` option. The serializer must synchronously encode and decode the entire storage envelope, including value, marker, version, and expiration metadata. Changing serializers can make existing entries unreadable; prefer a new prefix and migrate values explicitly. ```ts import { createStorage } from 'ultrastorage'; import superjson from 'superjson'; export const appStorage = createStorage({ prefix: 'acme', serializer: { stringify: superjson.stringify, parse: superjson.parse }, }); ``` -------------------------------- ### useStorage with schema and expiresAt in DailyCounter Source: https://ultrastorage.dev/reference/react Use when the storage instance comes from props or context, can change between renders, or for a one-off call. The example passes an UltraStorage instance to a component and uses useStorage with a Zod schema and defaultValue; the setter accepts an absolute expiresAt instead of a ttl. Expiration is lazy, so the counter does not rerender at midnight; use either ttl or expiresAt on a write, never both, and expiresAt: null removes expiration. ```tsx import type { UltraStorage } from 'ultrastorage'; import { useStorage } from 'ultrastorage/react'; import { z } from 'zod'; const CountSchema = z.number().int().nonnegative(); export function DailyCounter({ storage }: { storage: UltraStorage }) { const [count, setCount, removeCount] = useStorage(storage, 'count', { schema: CountSchema, defaultValue: 0, }); function increment() { const midnight = new Date(); midnight.setHours(24, 0, 0, 0); setCount((previous) => previous + 1, { expiresAt: midnight }); } return ( <> ); } ``` -------------------------------- ### keys() method signature Source: https://ultrastorage.dev/reference/storage Returns a fresh array of recognized, non-expired keys in the current namespace, in backend enumeration order. Scans the backend once and decodes each matching entry once. Expired and foreign entries are skipped without removing anything or notifying subscribers. ```ts appStorage.keys(): StorageKey[]; ``` -------------------------------- ### Change expiration with appStorage.setExpiration() Source: https://ultrastorage.dev/guides/expiration Use to reset a draft's lifetime without supplying its value. Pass exactly one of ttl (starts from now, not added to old deadline) or expiresAt, or null to remove expiration. Returns true for existing unexpired entries (including when deadline is already requested); missing, expired, and unreadable entries return false without cleanup. Invalid options throw TypeError; backend and serialization errors propagate; expired values are never revived; stored null counts as existing. Setting a past deadline makes the entry expire on next read; cleanup emits 'expire'; no timer is scheduled. ```ts appStorage.setExpiration('draft', { ttl: 7 * 24 * 60 * 60_000 }); appStorage.setExpiration('draft', { expiresAt: new Date('2030-01-01') }); appStorage.setExpiration('draft', null); // Keep the value indefinitely ``` -------------------------------- ### Subscribe to theme key changes Source: https://ultrastorage.dev/guides/subscriptions Subscribes to the 'theme' key and applies the theme on changes. The listener receives change details (key, type, source) and is not invoked immediately upon subscription. ```ts import { appStorage } from './lib/app-storage'; function applyTheme() { document.documentElement.dataset.theme = appStorage.getItem('theme') ?? 'system'; } const unsubscribe = appStorage.subscribe('theme', (change) => { console.log(change.key); // 'theme' console.log(change.type); // 'set' | 'remove' | 'expire' console.log(change.source); // 'local' | 'external' applyTheme(); }); applyTheme(); // Subscribing does not invoke the listener immediately. ``` -------------------------------- ### createStorageHook(storage: UltraStorage): StorageHook Source: https://ultrastorage.dev/reference/react Binds a storage instance and returns a hook that accepts (key, options?). The bound hook uses that instance's configured Storage object, prefix, separator, and serializer, with the same read options and return value as useStorage. ```APIDOC ## `createStorageHook(storage: UltraStorage): StorageHook` ### Description Binds a storage instance and returns a hook that accepts `(key, options?)`. The bound hook uses that instance's configured `Storage` object, prefix, separator, and serializer, with the same read options and return value as `useStorage`. ### Parameters - **storage** (`UltraStorage`) - Required - The storage instance to bind to the returned hook. ### Returns - **StorageHook** - A hook function that accepts `(key, options?)` and returns the same tuple as `useStorage`. ### Example Create a shared instance and export its bound hook: ```ts import { createStorage } from 'ultrastorage'; import { createStorageHook } from 'ultrastorage/react'; export const appStorage = createStorage({ prefix: 'app' }); export const useAppStorage = createStorageHook(appStorage); ``` Components can read and update the same key without passing the instance. This example uses Zod for the `schema` option, a display-only `defaultValue`, and `ttl` on a write: ```tsx 'use client'; import { z } from 'zod'; import { useAppStorage } from './lib/app-storage'; const ThemeSchema = z.enum(['light', 'dark']); export function ThemePicker() { const [theme, setTheme, removeTheme] = useAppStorage('theme', { schema: ThemeSchema, defaultValue: 'light', }); return ( <> ); } export function CurrentTheme() { const [theme] = useAppStorage('theme', { schema: ThemeSchema, defaultValue: 'light', }); return

Current theme: {theme}

; } ``` Mount both components anywhere in the app. Toggling the theme updates both, and resetting it removes the stored entry so both display `'light'`. They also respond to `appStorage.setItem('theme', 'dark')` outside React. Both consumers use the same schema and default so they agree on invalid or missing data. The schema validates stored values on read; the default is never automatically written. The one-hour button passes `ttl` to the setter. Expiration is lazy by default, so elapsed time alone does not trigger a render; see [snapshots and expiration](/guides/react#snapshots-and-expiration). ``` -------------------------------- ### Use namespaced storage: setItem, getItem, clear Source: https://ultrastorage.dev/guides/namespaces Shows how to use the namespaced storage instance. Keys are stored with the prefix, and clear() only removes keys within the namespace. ```TypeScript import { appStorage } from './lib/app-storage'; appStorage.setItem('theme', 'dark'); // stored as "acme:theme" appStorage.getItem('theme'); // 'dark' localStorage.getItem('theme'); // null // clear() only removes keys within the namespace appStorage.clear(); ``` -------------------------------- ### Use a component-supplied instance with useStorage Source: https://ultrastorage.dev/guides/react Use useStorage() when the caller chooses the instance. This component can work with any factory-created ultrastorage instance passed by its parent. Keep the supplied instance stable between renders unless you intend to change stores. ```tsx import type { UltraStorage } from 'ultrastorage'; import { useStorage } from 'ultrastorage/react'; export function ThemePicker({ storage }: { storage: UltraStorage }) { const [theme, setTheme] = useStorage(storage, 'theme', { defaultValue: 'light' }); return ( ); } ``` -------------------------------- ### Using string and array keys with appStorage Source: https://ultrastorage.dev/reference/storage Demonstrates using string and array keys. Array keys preserve structural segments and are serialized into an opaque canonical string. The getItem call returns the stored object. ```ts const userId = '42'; appStorage.setItem('theme', 'dark'); appStorage.setItem(['users', userId], { name: 'Alice', role: 'admin', }); appStorage.getItem(['users', userId]); // { name: 'Alice', role: 'admin' } ``` -------------------------------- ### Create storage with conditional memory fallback Source: https://ultrastorage.dev/guides/destinations Use this snippet to create a storage instance that uses localStorage in the browser and an in-memory storage on the server (e.g., for SSR). The `storage` option accepts any object implementing the `Storage` interface; passing `undefined` lets ultrastorage choose the default. ```ts import { createStorage, createMemoryStorage } from 'ultrastorage'; export const appStorage = createStorage({ prefix: 'acme', storage: typeof window === 'undefined' ? createMemoryStorage() : undefined, }); ``` -------------------------------- ### keys() Source: https://ultrastorage.dev/reference/storage Returns a fresh array of recognized, non-expired keys in the current namespace, in backend enumeration order. The method scans the backend once and decodes each matching entry once, skipping expired and foreign entries without removing them or notifying subscribers. ```APIDOC ## keys() ### Description Returns a fresh array of recognized, non-expired keys in the current namespace, in backend enumeration order. The method scans the backend once and decodes each matching entry once, skipping expired and foreign entries without removing them or notifying subscribers. ### Method keys() ### Endpoint appStorage.keys() ### Parameters None ### Request Example ```ts appStorage.keys(); ``` ### Response #### Success Response - **StorageKey[]** - A fresh array of keys relative to the prefix. Array keys are restored as fresh arrays containing their original segments; string keys remain strings except for reserved array encodings. #### Response Example ```ts ['theme', ['users', '42']] ``` ```