A library built on top of fabric.js that provides undo/redo history management for canvas operations.
- Undo/Redo functionality for fabric.js canvases
- Automatic history tracking for path creation, erasing events, object additions, removals, and modifications
- Multi-selection batching: operations on multiple selected objects are recorded as a single history entry
- Custom events for history state changes
- Fully type-safe
- Support for fabric.js v6 and v7
npm install @anth0nycodes/fabric-historyor with pnpm:
pnpm add @anth0nycodes/fabric-historyor with yarn:
yarn add @anth0nycodes/fabric-historyimport { CanvasWithHistory } from "@anth0nycodes/fabric-history";
import { Rect } from "fabric";
// Create a canvas with history support (same constructor as fabric.Canvas)
const canvas = new CanvasWithHistory("my-canvas", {
width: 800,
height: 600,
});
// Add objects - history is tracked automatically
const rect = new Rect({
left: 100,
top: 100,
width: 50,
height: 50,
fill: "red",
});
canvas.add(rect);
// Undo the last action
await canvas.undo();
// Redo the undone action
await canvas.redo();To enable history tracking for erasing operations, use the setEraserBrush method with an EraserBrush from @erase2d/fabric. This ensures that erasing actions trigger the erasing:end event required for history tracking.
import { CanvasWithHistory } from "@anth0nycodes/fabric-history";
import { EraserBrush } from "@erase2d/fabric";
const canvas = new CanvasWithHistory("my-canvas", {
width: 800,
height: 600,
});
// Create and set the eraser brush
const eraser = new EraserBrush(canvas);
eraser.width = 20;
canvas.setEraserBrush(eraser);
// Enable drawing mode to use the eraser
canvas.isDrawingMode = true;Set excludeFromExport: true on an object to prevent its changes from being saved to history.
const rect = new Rect({
left: 0,
top: 0,
width: 800,
height: 1,
fill: "blue",
excludeFromExport: true, // changes to this object won't be saved to history
});
canvas.add(guide);Extends fabric.js Canvas class with history management capabilities.
| Method | Returns | Description |
|---|---|---|
undo() |
Promise<void> |
Undo the most recent action |
redo() |
Promise<void> |
Redo the most recently undone action |
canUndo() |
boolean |
Check if an undo action is available |
canRedo() |
boolean |
Check if a redo action is available |
setEraserBrush(eraser) |
void |
Set an EraserBrush from @erase2d/fabric to enable history tracking for erasing operations |
clearHistory() |
void |
Clear the undo and redo history stacks |
clearCanvas() |
void |
Clear the canvas and save the cleared state to history (use this instead of the inherited clear()) |
dispose() |
void |
Clean up event listeners and dispose the canvas |
History is automatically saved when these fabric.js events occur:
path:created- When a path is created (e.g., freehand drawing)erasing:end- When an erasing operation completesobject:added- When an object is added to the canvasobject:removed- When an object is removed from the canvasobject:modified- When an object is modified (moved, scaled, rotated, etc.)
CanvasWithHistory fires custom events that you can listen to for history state changes:
| Event | Payload | Description |
|---|---|---|
history:append |
{ json: string, initial: boolean } |
Fired when a state is saved |
history:undo |
{ lastUndoAction: string } |
Fired when an undo is performed |
history:redo |
{ lastRedoAction: string } |
Fired when a redo is performed |
history:cleared |
{} |
Fired when history stacks cleared |
Example:
canvas.on("history:append", ({ json, initial }) => {
console.log("State saved:", initial ? "initial" : "action");
});
canvas.on("history:undo", ({ lastUndoAction }) => {
console.log("Undo performed");
});- fabric.js 6.x or 7.x
# Install dependencies
pnpm install
# Run development server
pnpm dev
# Build the project
pnpm build
# Type check
pnpm check
# Run all tests
pnpm test
# Run integration tests only
pnpm test:it
# Run E2E tests only (uses Playwright)
pnpm test:e2e
# Run tests with coverage
pnpm coverageContributions are welcome! Please read our CONTRIBUTING.md for details on how to submit pull requests.
MIT
Anthony Hoang