Back to projects

Easy History

A lightweight React hook for undo, redo, batch updates, and snapshotting.

TypeScriptReactJest

What it is

Easy History is a lightweight TypeScript library that adds a full undo/redo stack, batch commits, and point-in-time snapshots to any React state. It ships as a useHistory hook that's a direct replacement for useState.

What it does

The hook returns set, undo, redo, canUndo, canRedo, batch, takeSnapshot, and restoreSnapshot. Batch groups multiple state mutations into a single history entry. Snapshots are full copies of the entire stack — not just the current value — so you can jump to any previous checkpoint. History depth is configurable and a custom isEqual function controls when new entries are created.

Live Demo - Counter

8

Batch updates & snapshots

batch() lets you derive the next state from current state in a single history entry — useful for bulk edits like completing all tasks at once. takeSnapshot() captures the entire stack, not just the present value, so restoreSnapshot() brings back the full undo/redo history from that point. Try it below: edit the list, save a checkpoint, keep making changes, then restore.

Live demo — task list

1 / 3 done

  • Design the API
  • Write unit tests
  • Publish to npm

Even works with canvas!

Easy History can manage any state shape, including complex objects like canvas drawings. The Canvas Draw demo uses useHistory to track an array of stroke objects representing user drawings. Each stroke is a series of points, and the history stack captures every change, allowing full undo/redo functionality for freehand drawing.

Live Demo — Canvas Drawing

Why I built it

I kept reaching for undo/redo in side projects — canvas tools, form builders, config editors — and finding nothing lightweight enough. Every solution either pulled in a large state management library or required rewriting the component around a specific API. I wanted something I could drop next to an existing useState call without changing anything else.

Implementation

The core is a History class that manages past, present, and future stacks independently of React. The useHistory hook is a thin wrapper that syncs the class instance into component state on each mutation. Batch updates accumulate changes in a local transaction before committing a single entry to the stack. Snapshots deep-clone the full stack state, not just the present value.