fabricjs/fabric.js · error · FabricError

Trying to initialize a canvas that has already been initiali

Error message

Trying to initialize a canvas that has already been initialized. Did you forget to dispose the canvas?

What it means

Fabric.js marks the canvas element it initializes with a `data-fabric="main"` attribute so a canvas is never initialized twice. When you construct a `StaticCanvas`/`FabricCanvas` over an element that already carries this attribute (from a previous, undisposed instance), this error is thrown to prevent corrupting internal state and leaking listeners.

Source

Thrown at packages/core/src/canvas/DOMManagers/StaticCanvasDOMManager.ts:37

   */
  private _originalCanvasStyle?: string;

  lower: CanvasItem;

  constructor(arg0?: string | HTMLCanvasElement) {
    const el = this.createLowerCanvas(arg0);
    this.lower = { el, ctx: el.getContext('2d')! };
  }

  protected createLowerCanvas(arg0?: HTMLCanvasElement | string) {
    // canvasEl === 'HTMLCanvasElement' does not work on jsdom/node
    const el = isHTMLCanvas(arg0)
      ? arg0
      : (arg0 &&
          (getFabricDocument().getElementById(arg0) as HTMLCanvasElement)) ||
        createCanvasElement();
    if (el.hasAttribute('data-fabric')) {
      throw new FabricError(
        'Trying to initialize a canvas that has already been initialized. Did you forget to dispose the canvas?',
      );
    }
    this._originalCanvasStyle = el.style.cssText;
    el.setAttribute('data-fabric', 'main');
    el.classList.add('lower-canvas');
    return el;
  }

  cleanupDOM({ width, height }: TSize) {
    const { el } = this.lower;
    // restore canvas style and attributes
    el.classList.remove('lower-canvas');
    el.removeAttribute('data-fabric');
    // restore canvas size to original size in case retina scaling was applied
    el.setAttribute('width', `${width}`);
    el.setAttribute('height', `${height}`);
    el.style.cssText = this._originalCanvasStyle || '';

View on GitHub (pinned to 2bd4992cab)

Solutions

  1. Dispose the previous instance before re-initializing: keep a ref and call `canvas.dispose()` in cleanup (e.g. React useEffect return, Vue onBeforeUnmount).
  2. In React StrictMode/dev double-mount scenarios, make the effect idempotent: dispose on every cleanup run, not just unmount.
  3. If the element is stale and you truly want a fresh canvas, remove the attribute first: `el.removeAttribute('data-fabric')` (only when you know no live instance owns it), or render a brand-new <canvas> element.
  4. Initialize from a fresh element or id that isn't already managed by Fabric.

Example fix

// before (React)
useEffect(() => {
  const canvas = new fabric.Canvas(ref.current); // throws on StrictMode remount
}, []);

// after
useEffect(() => {
  const canvas = new fabric.Canvas(ref.current);
  return () => { canvas.dispose(); };
}, []);
Defensive patterns

Strategy: validation

Validate before calling

const el = document.getElementById('c') as HTMLCanvasElement;
if (el.hasAttribute('data-fabric')) {
  // previous instance still owns this element
  previousCanvas?.dispose();
  el.removeAttribute('data-fabric'); // only if no live instance remains
}
const canvas = new fabric.Canvas(el);

Type guard

const isFabricManaged = (el: HTMLCanvasElement): boolean =>
  el.hasAttribute('data-fabric');

Try / catch

try {
  canvas = new fabric.Canvas(el);
} catch (e) {
  if (e instanceof Error && e.message.includes('already been initialized')) {
    await oldInstance?.dispose();
    canvas = new fabric.Canvas(el); // retry on the freed element
  } else throw e;
}

Prevention

When it happens

Trigger: Creating `new fabric.Canvas(existingEl)` twice on the same DOM canvas without calling `dispose()` in between; re-initializing after a hot-module reload or framework re-render (React/Vue) that reuses the same canvas element; re-mounting a component that queries the canvas by id while the old instance was never disposed.

Common situations: React StrictMode double-mounting in development; forgetting `canvas.dispose()` in a cleanup hook; HMR re-running initialization code against the same element; SSR/hydration reusing the server-rendered canvas element that a client instance already claimed.

Related errors


AI-assisted analysis of fabricjs/fabric.js@2bd4992cab (2026-08-28). Data as JSON: /api/errors/dfc588eedb445949. Report an issue: GitHub.