Skip to main content
Version: 1.3.0

Custom Storage

Mnemonic defaults to localStorage but accepts any backend that implements the StorageLike interface.

Architecture note: StorageLike is sync-only

StorageLike is intentionally synchronous in v1. Mnemonic reads storage through React's synchronous snapshot contract, so getItem, setItem, and removeItem must all finish immediately.

That means AsyncStorage-style adapters are not a drop-in fit for this surface. If your real persistence layer is async, wrap it behind a synchronous in-memory cache and let that cache satisfy StorageLike. Promise-returning methods are unsupported and are treated as a runtime contract violation.

The StorageLike interface

interface StorageLike {
getItem(key: string): string | null;
setItem(key: string, value: string): void;
removeItem(key: string): void;
key?(index: number): string | null;
readonly length?: number;
onExternalChange?: (callback: (changedKeys?: string[]) => void) => () => void;
}

Only getItem, setItem, and removeItem are required. The optional properties enable additional features:

PropertyPurpose
key(index)Enables DevTools key enumeration
lengthEnables DevTools key enumeration
onExternalChangeCross-tab sync for non-localStorage backends

Example: IndexedDB with BroadcastChannel

import { MnemonicProvider } from "react-mnemonic";
import type { StorageLike } from "react-mnemonic";

const idbStorage: StorageLike = {
getItem: (key) => {
// synchronous read from an in-memory cache
// populated from IndexedDB on startup
return cache.get(key) ?? null;
},
setItem: (key, value) => {
cache.set(key, value);
// async write to IDB in background
idb.put("store", value, key);
},
removeItem: (key) => {
cache.delete(key);
idb.delete("store", key);
},
onExternalChange: (cb) => {
const bc = new BroadcastChannel("my-app-sync");
bc.onmessage = (e) => cb(e.data.keys);
return () => bc.close();
},
};

function App() {
return (
<MnemonicProvider namespace="my-app" storage={idbStorage}>
{/* components */}
</MnemonicProvider>
);
}

This pattern keeps the hook contract synchronous while still letting IndexedDB handle durability in the background.

sessionStorage

For session-scoped persistence, just pass sessionStorage:

<MnemonicProvider namespace="session" storage={sessionStorage}>
<App />
</MnemonicProvider>

Error handling

The library handles all storage errors internally — failures are logged but never thrown to your components. See the StorageLike JSDoc for the full error-handling contract.