{"record":{"id":"cba33e335559d592","repo":"pmndrs/react-three-fiber","slug":"r3f-hooks-can-only-be-used-within-the-canvas-comp","errorCode":null,"errorMessage":"R3F: Hooks can only be used within the Canvas component!","messagePattern":"R3F: Hooks can only be used within the Canvas component!","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/fiber/src/core/hooks.tsx","lineNumber":26,"sourceCode":"/**\n * Exposes an object's {@link Instance}.\n * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useInstanceHandle\n *\n * **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.\n */\nexport function useInstanceHandle<T>(ref: React.RefObject<T>): React.RefObject<Instance<T>> {\n  const instance = React.useRef<Instance>(null!)\n  React.useImperativeHandle(instance, () => (ref.current as unknown as Instance<T>['object']).__r3f!, [ref])\n  return instance\n}\n\n/**\n * Returns the R3F Canvas' Zustand store. Useful for [transient updates](https://github.com/pmndrs/zustand#transient-updates-for-often-occurring-state-changes).\n * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usestore\n */\nexport function useStore(): RootStore {\n  const store = React.useContext(context)\n  if (!store) throw new Error('R3F: Hooks can only be used within the Canvas component!')\n  return store\n}\n\n/**\n * Accesses R3F's internal state, containing renderer, canvas, scene, etc.\n * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usethree\n */\nexport function useThree<T = RootState>(\n  selector: (state: RootState) => T = (state) => state as unknown as T,\n  equalityFn?: <T>(state: T, newState: T) => boolean,\n): T {\n  return useStore()(selector, equalityFn)\n}\n\n/**\n * Executes a callback before render in a shared frame loop.\n * Can order effects with render priority or manually render with a positive priority.\n * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/pmndrs/react-three-fiber/blob/ff3899dbf43d2a88895fecf53c147192abfd7431/packages/fiber/src/core/hooks.tsx#L8-L44","documentation":"All react-three-fiber hooks (useThree, useFrame, useStore, etc.) read the Canvas's Zustand store from React context. If a hook runs outside a <Canvas> subtree, the context value is undefined and R3F throws this guard error immediately. The check protects against silently broken hooks that would otherwise return undefined state and crash later in confusing ways.","triggerScenarios":"Calling useThree(), useFrame(), useStore(), or any hook built on them (useLoader results access, events, raycast helpers) in a component rendered as a normal React child instead of inside <Canvas>...</Canvas>. Also happens when a component reads the store during module-level code or in a plain ReactDOM.render tree.","commonSituations":"Rendering an R3F scene component via ReactDOM into a DOM root (e.g. in Storybook or a test) without the Canvas wrapper; extracting a shared hook into a file used by both DOM UI and 3D components; hooks placed in a parent of <Canvas> rather than a child; HMR or refactoring accidentally moving a component outside the Canvas subtree.","solutions":["Move the component using R3F hooks inside the <Canvas> element's children: <Canvas><MyScene /></Canvas>.","If the component must live outside Canvas (e.g. DOM UI), pass the needed state down from a child inside Canvas, or use createPortal/Html-style patterns instead of the hooks.","In tests, wrap the component in the test-renderer's Canvas equivalent (react-three-test-renderer's <Canvas> or createRoot(...).render) rather than @testing-library's DOM render.","Double-check conditional rendering: a component that sometimes renders inside Canvas and sometimes not will throw only on the 'not' path."],"exampleFix":"// before\nfunction App() {\n  const { camera } = useThree() // Error: hooks can only be used within the Canvas\n  return <><DomUi /><Scene /></>\n}\n\n// after\nfunction App() {\n  return (\n    <Canvas>\n      <Rig /> {/* useThree/useFrame live here */}\n      <Scene />\n    </Canvas>\n  )\n}","handlingStrategy":"type-guard","validationCode":"null","typeGuard":"import { isInsideCanvas } from './guards' // hypothetical; practically: check via context consumer\n// Practical guard: split components so R3F hooks only exist in Canvas children\n// e.g. keep useFrame/useThree in <SceneContents /> rendered inside <Canvas>","tryCatchPattern":"try {\n  const state = useThree() // note: hooks must not be called conditionally\n} catch (e) {\n  // Not viable: never wrap hook calls in try/catch; restructure the tree instead\n  throw e\n}","preventionTips":["Keep a rule: any component using useThree/useFrame/useStore is only ever rendered as a descendant of <Canvas>.","Use ESLint eslint-plugin-react-hooks to catch conditional hook usage that can escape the Canvas subtree.","In Storybook/tests, use the R3F Canvas (or react-three-test-renderer) as the render root."],"tags":["react-three-fiber","hooks","context","canvas","react-context"],"backgroundTag":"react-hook-outside-provider","analyzedSha":"ff3899dbf43d2a88895fecf53c147192abfd7431","analyzedAt":"2026-08-28T10:16:38.568Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}