react-amnesia
v0.3.0
Published
AI-friendly application undo/redo (CTRL+Z) for React
Maintainers
Readme
react-amnesia
AI-friendly application undo/redo (Ctrl+Z) for React.

react-amnesia is a small, in-memory command-history store for React
applications. It manages an undo / redo stack and ships a useState-shaped
hook so any piece of state in your UI can become reversible. Like its sister
project react-mnemonic, it
is designed to be AI-first: visible structure, unambiguous specifications,
and small, composable primitives that an agent can reason about without
guessing.
react-amnesia works well alongside react-mnemonic (so undoable state can
also survive page reloads) but does not require it.
Installation
npm install react-amnesiaReact 18 or 19 is required (^18.0.0 || ^19.0.0). The library is tested against both versions under <StrictMode>.
Quick start
Wrap the part of your tree that should share an undo stack in
AmnesiaProvider, drop an AmnesiaShortcuts somewhere inside it for
keyboard bindings, then use useUndoableState for any value the user can
edit.
import { AmnesiaProvider, AmnesiaShortcuts, useUndoableState } from "react-amnesia";
function TitleEditor() {
const [title, setTitle] = useUndoableState("Untitled", {
label: "Edit title",
coalesceKey: "edit:title",
});
return <input value={title} onChange={(event) => setTitle(event.target.value)} />;
}
export default function App() {
return (
<AmnesiaProvider capacity={200}>
<AmnesiaShortcuts />
<TitleEditor />
</AmnesiaProvider>
);
}Ctrl+Z / Cmd+Z undoes the last edit; Ctrl+Shift+Z / Cmd+Shift+Z /
Ctrl+Y redoes. Rapid keystrokes that share a coalesceKey collapse into a
single history entry, so a single undo reverts the entire burst.
Why use it
useState-shaped hook (useUndoableState) for reversible UI state- Imperative
push({ redo, undo, label })for arbitrary actions useAmnesiaLabels(scopeId?)selector for stable Undo/Redo menu labels- Coalescing for keystroke / drag bursts
- Capacity-bounded stack so history can't grow without limit
- Standard keyboard bindings via a single
<AmnesiaShortcuts />element - Optional
react-mnemonicintegration for persistence-aware undoables - Zero runtime dependencies; published TypeScript types
Pick the right entrypoint
| Entrypoint | Use when |
| ------------------------ | -------------------------------------------------------------------------------------- |
| react-amnesia/core | Pure undo/redo runtime. No react-mnemonic dependency. |
| react-amnesia/mnemonic | You want usePersistedUndoableState to combine undo with react-mnemonic storage. |
| react-amnesia/native | You need native editable detection / document.execCommand("undo" \| "redo") helpers. |
| react-amnesia | Top-level entrypoint that re-exports core. Drop-in default for most apps. |
Imperative commands
For actions that don't fit a single value (e.g. inserting / deleting list items, mutating a graph, applying a transform), push commands directly.
import { useAmnesia } from "react-amnesia";
function AddItemButton({ list }: { list: List }) {
const { push } = useAmnesia();
return (
<button
onClick={() => {
const item = list.createItem();
push({
label: "Add item",
redo: () => list.add(item),
undo: () => list.remove(item.id),
});
}}
>
Add
</button>
);
}push calls redo() once on insertion. If your call site has already mutated
the application state, pass { applied: true } to skip that initial run.
Optional persistence
When paired with react-mnemonic, usePersistedUndoableState reads and
writes the value through useMnemonicKey while still recording each user
edit on the local Amnesia stack:
import { MnemonicProvider } from "react-mnemonic";
import { AmnesiaProvider, AmnesiaShortcuts } from "react-amnesia";
import { usePersistedUndoableState } from "react-amnesia/mnemonic";
function ThemePicker() {
const { value, set } = usePersistedUndoableState<"light" | "dark">("theme", {
defaultValue: "light",
label: "Change theme",
});
return (
<select value={value} onChange={(e) => set(e.target.value as "light" | "dark")}>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
);
}
export default function App() {
return (
<MnemonicProvider namespace="my-app">
<AmnesiaProvider>
<AmnesiaShortcuts />
<ThemePicker />
</AmnesiaProvider>
</MnemonicProvider>
);
}The undo stack itself is intentionally not persisted. Closures aren't serializable, and replaying old commands against new state is usually the wrong default. Reloads keep the latest value, but the history starts fresh on each session.
AI resources
| Resource | Purpose |
| ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------- |
| AI Docs | Canonical invariants, decision matrix, recipes, anti-patterns, and setup guidance |
| llms.txt | Compact retrieval index for tight context windows |
| llms-full.txt | Long-form export for indexing and larger prompt contexts |
| ai-contract.json | Machine-readable runtime contract for tooling and agent integrations |
| DeepWiki priorities | Steering file that points DeepWiki toward the highest-signal sources |
| AI Assistant Setup | Generated instruction packs plus the documented MCP-friendly retrieval path |
Learn more
- Documentation home
- Quick Start
- Keyboard Shortcuts
- Coalescing Bursts
- Imperative Commands
- Async Commands
- Transactions
- Multi-Scope Routing
- DevTools
- OS Menu Integration (Tauri/Electron)
- Error Handling
- API Reference
