{"record":{"id":"50140079d7a0fa8a","repo":"pmndrs/react-three-fiber","slug":"r3f-createroot-should-only-be-called-once","errorCode":null,"errorMessage":"R3F.createRoot should only be called once!","messagePattern":"R3F\\.createRoot should only be called once!","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/fiber/src/core/renderer.tsx","lineNumber":150,"sourceCode":"      width: canvas.width,\n      height: canvas.height,\n      top: 0,\n      left: 0,\n    }\n  }\n\n  return { width: 0, height: 0, top: 0, left: 0, ...size }\n}\n\nexport function createRoot<TCanvas extends HTMLCanvasElement | OffscreenCanvas>(\n  canvas: TCanvas,\n): ReconcilerRoot<TCanvas> {\n  // Check against mistaken use of createRoot\n  const prevRoot = _roots.get(canvas)\n  const prevFiber = prevRoot?.fiber\n  const prevStore = prevRoot?.store\n\n  if (prevRoot) console.warn('R3F.createRoot should only be called once!')\n\n  // Report when an error was detected in a previous render\n  // https://github.com/pmndrs/react-three-fiber/pull/2261\n  const logRecoverableError =\n    typeof reportError === 'function'\n      ? // In modern browsers, reportError will dispatch an error event,\n        // emulating an uncaught JavaScript error.\n        reportError\n      : // In older browsers and test environments, fallback to console.error.\n        console.error\n\n  // Create store\n  const store = prevStore || createStore(invalidate, advance)\n  // Create renderer\n  const fiber =\n    prevFiber ||\n    (reconciler as any).createContainer(\n      store, // container","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/pmndrs/react-three-fiber/blob/ff3899dbf43d2a88895fecf53c147192abfd7431/packages/fiber/src/core/renderer.tsx#L132-L168","documentation":"R3F keeps a registry of roots per canvas element; calling createRoot on a canvas that already has a root warns 'R3F.createRoot should only be called once!' because the previous root's state (scene, store, subscriptions) would be orphaned. It is a console warning, not a throw, but usually indicates a leak or double-mount.","triggerScenarios":"Calling createRoot(canvas) twice on the same <canvas> DOM node — typically from StrictMode double-effect in React 18/19, or from a component whose effect creates/destroys the root without cleanup, or manually re-initializing the renderer.","commonSituations":"React.StrictMode in development double-invoking effects around <Canvas>, HMR remounting the canvas component, an effect missing its cleanup function, or version upgrades to React 18+ where effects run twice in dev.","solutions":["Ensure the code that creates the root has a matching cleanup (unmount) in the effect","Remove accidental duplicate <Canvas> elements sharing the same canvas DOM node","In StrictMode, ensure you're on an @react-three/fiber version compatible with React 18/19 double-mounting semantics","Key the canvas node or reuse the existing root via _roots instead of re-calling createRoot"],"exampleFix":"// before\nuseEffect(() => {\n  const root = createRoot(canvasEl)\n  root.render(<Scene />)\n})\n\n// after\nuseEffect(() => {\n  const root = createRoot(canvasEl)\n  root.render(<Scene />)\n  return () => root.unmount()\n}, [])","handlingStrategy":"validation","validationCode":"import { _roots } from '@react-three/fiber' // or track roots yourself\nconst existing = trackedRoots.get(canvasEl)\nif (existing) { existing.render(<Scene />) } else { trackedRoots.set(canvasEl, createRoot(canvasEl)) }","typeGuard":"const hasExistingRoot = (canvas: HTMLCanvasElement): boolean => trackedRoots.has(canvas)","tryCatchPattern":null,"preventionTips":["Always return a cleanup that unmounts the root from effects","Use a single <Canvas> per canvas DOM node; don't manually createRoot over it","Test with StrictMode locally to catch double-mount leaks early"],"tags":["create-root","double-mount","strictmode","lifecycle","memory-leak"],"backgroundTag":"root-created-twice","analyzedSha":"ff3899dbf43d2a88895fecf53c147192abfd7431","analyzedAt":"2026-08-28T10:16:38.568Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}