===============
LIBRARY RULES
===============
From library maintainers:
- Prefer website/docs/ai as the canonical source for persistence semantics, invariants, and decision rules.
- Treat README.md and src/index.ts as the public package surface and prefer published entrypoints over internal paths.
- Use react-mnemonic/core for the lean persisted-state path and react-mnemonic/schema when validation, versioning, or migrations are required.
- Durable app state should use MnemonicProvider and useMnemonicKey rather than raw localStorage in consumer-facing code.
- Treat excluded build outputs as derived artifacts rather than source-of-truth documentation.
# React Mnemonic
React Mnemonic is an AI-friendly, persistent, type-safe state management library for React applications. It provides a `useState`-like API through the `useMnemonicKey` hook that persists values to localStorage (or custom storage backends), surviving page reloads and optionally syncing across browser tabs. The library is built with SSR-safety in mind and requires zero runtime dependencies.
The library offers three entrypoints: `react-mnemonic/core` for lean persisted-state functionality, `react-mnemonic/schema` for JSON Schema validation, versioning, and migrations, and the root `react-mnemonic` for full backward compatibility. Values are stored in versioned envelopes that enable automatic schema migrations when data shapes evolve between app versions.
## MnemonicProvider
The `MnemonicProvider` component creates a namespaced storage scope for child components. All keys written by hooks inside the provider are prefixed with the namespace to prevent collisions between different parts of your application.
```tsx
import { MnemonicProvider, useMnemonicKey } from "react-mnemonic/core";
function Counter() {
const { value: count, set } = useMnemonicKey("count", {
defaultValue: 0,
});
return (
Count: {count}
);
}
export default function App() {
return (
);
}
// The counter persists in localStorage as "my-app.count"
// and survives full page reloads
```
## useMnemonicKey
The `useMnemonicKey` hook provides a `useState`-like API for persistent state. It returns an object with `value`, `set`, `reset`, and `remove` functions for managing persisted data.
```tsx
import { useMnemonicKey } from "react-mnemonic/core";
interface UserPreferences {
theme: "light" | "dark";
fontSize: number;
notifications: boolean;
}
function SettingsPanel() {
const { value: prefs, set, reset, remove } = useMnemonicKey("preferences", {
defaultValue: {
theme: "light",
fontSize: 14,
notifications: true,
},
onChange: (newPrefs, oldPrefs) => {
console.log("Preferences changed from", oldPrefs, "to", newPrefs);
},
onMount: (initialPrefs) => {
document.body.className = initialPrefs.theme;
},
});
return (
);
}
```
## defineMnemonicKey
The `defineMnemonicKey` function creates reusable, importable key descriptors that package the storage key and its options into a stable object. This helps keep persistence behavior explicit and consistent across components.
```tsx
import { defineMnemonicKey, useMnemonicKey, MnemonicProvider } from "react-mnemonic/core";
// Define once at module scope
const themeKey = defineMnemonicKey("theme", {
defaultValue: "light" as "light" | "dark" | "system",
listenCrossTab: true,
});
const sidebarKey = defineMnemonicKey("sidebar-collapsed", {
defaultValue: false,
});
// Reuse across components
function Header() {
const { value: theme, set } = useMnemonicKey(themeKey);
return (
);
}
function Sidebar() {
const { value: collapsed, set } = useMnemonicKey(sidebarKey);
return (
);
}
```
## Cross-Tab Synchronization
Enable cross-tab synchronization with the `listenCrossTab` option so that changes in one browser tab are reflected in all other tabs reading the same key.
```tsx
import { useMnemonicKey } from "react-mnemonic/core";
function ThemeSwitcher() {
const { value: theme, set } = useMnemonicKey<"light" | "dark">("theme", {
defaultValue: "light",
listenCrossTab: true,
onChange: (newTheme) => {
// Update document when theme changes (including from other tabs)
document.documentElement.setAttribute("data-theme", newTheme);
},
});
return (
);
}
// Changes in one tab automatically sync to all other tabs
```
## Custom Storage Backends
Provide a custom synchronous storage backend by implementing the `StorageLike` interface. This enables use of sessionStorage, in-memory storage for testing, or custom adapters.
```tsx
import { MnemonicProvider, useMnemonicKey, type StorageLike } from "react-mnemonic/core";
// In-memory storage for testing
const mockStorage: StorageLike = {
items: new Map(),
getItem(key) {
return this.items.get(key) ?? null;
},
setItem(key, value) {
this.items.set(key, value);
},
removeItem(key) {
this.items.delete(key);
},
get length() {
return this.items.size;
},
key(index) {
return Array.from(this.items.keys())[index] ?? null;
},
};
// Custom storage with BroadcastChannel for cross-tab sync
const idbCacheStorage: StorageLike = {
cache: new Map(),
getItem(key) {
return this.cache.get(key) ?? null;
},
setItem(key, value) {
this.cache.set(key, value);
// Notify other tabs
new BroadcastChannel("app-sync").postMessage({ keys: [key] });
},
removeItem(key) {
this.cache.delete(key);
},
onExternalChange: (callback) => {
const bc = new BroadcastChannel("app-sync");
bc.onmessage = (e) => callback(e.data.keys);
return () => bc.close();
},
};
function App() {
return (
);
}
```
## Custom Codecs
Create custom codecs using `createCodec` for types that need special serialization, such as `Date`, `Set`, or `Map`. Using a custom codec bypasses JSON Schema validation.
```tsx
import { createCodec, useMnemonicKey, CodecError } from "react-mnemonic/core";
// Date codec
const DateCodec = createCodec(
(date) => date.toISOString(),
(str) => new Date(str)
);
// Set codec
const StringSetCodec = createCodec>(
(set) => JSON.stringify([...set]),
(str) => new Set(JSON.parse(str))
);
// Map codec
const MapCodec = createCodec