# Plain JavaScript

> Use this engine with Fabric.js plain JavaScript, without a framework: wire a toolbar to undo, redo and save with events or createDocumentStateStore.

Source: https://fabricjs-document-engine.jscrate.dev/docs/frameworks/vanilla-js
Last updated: 2026-09-28

The engine has no framework code in its core, so Fabric.js plain JavaScript works as well as any framework. You create the canvas, create the engine, and update the toolbar from its state.

## The page

```html
<div role="toolbar">
  <button id="undo">Undo</button>
  <button id="redo">Redo</button>
  <button id="save">Save</button>
  <span id="status" aria-live="polite"></span>
</div>
<canvas id="editor"></canvas>
```

## The script

```ts
import { Canvas } from "fabric";
import {
  bindKeyboardShortcuts,
  createDocumentEngine,
  createDocumentStateStore,
} from "fabricjs-document-engine";
import { createLocalStorage } from "fabricjs-document-engine/storage";

const canvas = new Canvas("editor", { width: 800, height: 500 });
const engine = createDocumentEngine({
  canvas,
  storage: createLocalStorage(),
  autosave: true,
});
bindKeyboardShortcuts(engine);

const undo = document.querySelector<HTMLButtonElement>("#undo")!;
const redo = document.querySelector<HTMLButtonElement>("#redo")!;
const save = document.querySelector<HTMLButtonElement>("#save")!;
const status = document.querySelector("#status")!;

undo.addEventListener("click", () => engine.undo());
redo.addEventListener("click", () => engine.redo());
save.addEventListener("click", () => engine.save());

const store = createDocumentStateStore(engine);

function render() {
  const state = store.getSnapshot();
  undo.disabled = !state.canUndo;
  redo.disabled = !state.canRedo;
  save.disabled = !state.isDirty;
  status.textContent = state.saveStatus;
}

store.subscribe(render);
render();
```

`createDocumentStateStore` gives you one snapshot with everything a toolbar needs. It only listens to the engine while it has subscribers.

## Events instead of a store

If you only need one value, listen to a single event instead:

```ts
engine.on("history:change", ({ canUndo, canRedo }) => {
  undo.disabled = !canUndo;
  redo.disabled = !canRedo;
});
```

`on` returns an unsubscribe function. The full list is in [events](https://fabricjs-document-engine.jscrate.dev/docs/api/events).

## Without a bundler

Fabric.js plain JavaScript also works straight from a CDN with an import map. The package is published as ES modules:

```html
<script type="importmap">
  {
    "imports": {
      "fabric": "https://cdn.jsdelivr.net/npm/fabric@7/+esm",
      "fabricjs-document-engine": "https://cdn.jsdelivr.net/npm/fabricjs-document-engine@1/+esm"
    }
  }
</script>
```

For a step-by-step build without a framework, follow the [editor tutorial](https://fabricjs-document-engine.jscrate.dev/docs/overview/tutorial).
