Undo / Redo
Ctrl+Z/Ctrl+Y history for forms with useFormHistory
Overview
No form library offers built-in undo/redo. useFormHistory() fills that gap with debounced snapshots, keyboard shortcuts, and an optional visual controls component.
import { HistoryControls, useFormHistory } from '@letar/forms'
function ProductEditor() {
const form = useDeclarativeForm()
const history = useFormHistory(form)
return (
<Form schema={ProductSchema} onSubmit={save}>
<HistoryControls history={history} />
<Form.Field.String name="title" />
<Form.Field.RichText name="description" />
<Form.Field.Currency name="price" />
<Form.Button.Submit>Save</Form.Button.Submit>
</Form>
)
}How It Works
- Subscribes to
form.store(TanStack Form reactive store) - On change → debounce 500ms →
structuredClonesnapshot - Undo/Redo applies snapshot via
form.setFieldValueper field - Undo/redo changes are not recorded in history (prevents recursion)
- Branching (undo → edit) trims future entries
API: useFormHistory(form, config?)
const history = useFormHistory(form, {
maxHistory: 50, // Max snapshots (default: 50)
debounceMs: 500, // Delay before recording (default: 500ms)
keyboard: true, // Ctrl+Z/Y shortcuts (default: true)
persist: false, // sessionStorage (default: false)
persistKey: 'form-history',
})Returns:
| Field | Type | Description |
|---|---|---|
undo | () => void | Undo last change |
redo | () => void | Redo undone change |
canUndo | boolean | Has undo history |
canRedo | boolean | Has redo history |
currentIndex | number | Position in stack |
historyLength | number | Total snapshots |
clear | () => void | Reset history |
Keyboard Shortcuts
| Keys | Action |
|---|---|
Ctrl+Z / Cmd+Z | Undo |
Ctrl+Shift+Z / Ctrl+Y | Redo |
Disable: keyboard: false.
HistoryControls
<HistoryControls
history={historyApi}
showCounter // Show "3/7" counter
size="sm" // xs | sm | md
/>When to Use
- CMS / content editors (many fields, frequent edits)
- Product configurators
- Forms with RichText (users expect Ctrl+Z)
- Long forms (10+ fields) where mistakes are costly