{"record":{"id":"dfc588eedb445949","repo":"fabricjs/fabric.js","slug":"trying-to-initialize-a-canvas-that-has-already-bee","errorCode":null,"errorMessage":"Trying to initialize a canvas that has already been initialized. Did you forget to dispose the canvas?","messagePattern":"Trying to initialize a canvas that has already been initialized\\. Did you forget to dispose the canvas\\?","errorType":"exception","errorClass":"FabricError","httpStatus":null,"severity":"error","filePath":"packages/core/src/canvas/DOMManagers/StaticCanvasDOMManager.ts","lineNumber":37,"sourceCode":"   */\n  private _originalCanvasStyle?: string;\n\n  lower: CanvasItem;\n\n  constructor(arg0?: string | HTMLCanvasElement) {\n    const el = this.createLowerCanvas(arg0);\n    this.lower = { el, ctx: el.getContext('2d')! };\n  }\n\n  protected createLowerCanvas(arg0?: HTMLCanvasElement | string) {\n    // canvasEl === 'HTMLCanvasElement' does not work on jsdom/node\n    const el = isHTMLCanvas(arg0)\n      ? arg0\n      : (arg0 &&\n          (getFabricDocument().getElementById(arg0) as HTMLCanvasElement)) ||\n        createCanvasElement();\n    if (el.hasAttribute('data-fabric')) {\n      throw new FabricError(\n        'Trying to initialize a canvas that has already been initialized. Did you forget to dispose the canvas?',\n      );\n    }\n    this._originalCanvasStyle = el.style.cssText;\n    el.setAttribute('data-fabric', 'main');\n    el.classList.add('lower-canvas');\n    return el;\n  }\n\n  cleanupDOM({ width, height }: TSize) {\n    const { el } = this.lower;\n    // restore canvas style and attributes\n    el.classList.remove('lower-canvas');\n    el.removeAttribute('data-fabric');\n    // restore canvas size to original size in case retina scaling was applied\n    el.setAttribute('width', `${width}`);\n    el.setAttribute('height', `${height}`);\n    el.style.cssText = this._originalCanvasStyle || '';","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/fabricjs/fabric.js/blob/2bd4992cabf4ec9609aa349ea09b723cadc94bef/packages/core/src/canvas/DOMManagers/StaticCanvasDOMManager.ts#L19-L55","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Dispose the previous instance before re-initializing: keep a ref and call `canvas.dispose()` in cleanup (e.g. React useEffect return, Vue onBeforeUnmount).","In React StrictMode/dev double-mount scenarios, make the effect idempotent: dispose on every cleanup run, not just unmount.","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.","Initialize from a fresh element or id that isn't already managed by Fabric."],"exampleFix":"// before (React)\nuseEffect(() => {\n  const canvas = new fabric.Canvas(ref.current); // throws on StrictMode remount\n}, []);\n\n// after\nuseEffect(() => {\n  const canvas = new fabric.Canvas(ref.current);\n  return () => { canvas.dispose(); };\n}, []);","handlingStrategy":"validation","validationCode":"const el = document.getElementById('c') as HTMLCanvasElement;\nif (el.hasAttribute('data-fabric')) {\n  // previous instance still owns this element\n  previousCanvas?.dispose();\n  el.removeAttribute('data-fabric'); // only if no live instance remains\n}\nconst canvas = new fabric.Canvas(el);","typeGuard":"const isFabricManaged = (el: HTMLCanvasElement): boolean =>\n  el.hasAttribute('data-fabric');","tryCatchPattern":"try {\n  canvas = new fabric.Canvas(el);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('already been initialized')) {\n    await oldInstance?.dispose();\n    canvas = new fabric.Canvas(el); // retry on the freed element\n  } else throw e;\n}","preventionTips":["Always dispose canvas instances in framework cleanup hooks (useEffect return, onBeforeUnmount).","Store the canvas instance in a ref/variable so dispose is always reachable.","In dev StrictMode, accept the double mount and dispose in every cleanup run.","Prefer rendering a fresh <canvas> element per mount instead of reusing one by id."],"tags":["fabricjs","canvas","double-initialization","react","dispose"],"backgroundTag":"resource-already-initialized","analyzedSha":"2bd4992cabf4ec9609aa349ea09b723cadc94bef","analyzedAt":"2026-08-28T10:56:44.286Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}