pmndrs/react-three-fiber · error · Error
R3F: ${name} is not part of the THREE namespace! Did you for
Error message
R3F: ${name} is not part of the THREE namespace! Did you forget to extend? See: https://docs.pmnd.rs/react-three-fiber/api/objects#using-3rd-party-objects-declaratively What it means
react-three-fiber resolves JSX element names (e.g. <orbitControls>, <mesh>) by looking up the PascalCase name in a catalogue of THREE.* classes. If the name isn't a member of the THREE namespace (or hasn't been registered), the reconciler throws this validation error during createInstance/commitUpdate instead of rendering 'undefined' elements. Third-party objects must be registered explicitly because they don't ship inside the THREE namespace.
Source
Thrown at packages/fiber/src/core/reconciler.tsx:179
objects: T,
): React.ExoticComponent<ThreeElement<any>> | void {
if (isConstructor(objects)) {
const Component = `${i++}`
catalogue[Component] = objects
return Component as any
} else {
Object.assign(catalogue, objects)
}
}
function validateInstance(type: string, props: HostConfig['props']): void {
// Get target from catalogue
const name = toPascalCase(type)
const target = catalogue[name]
// Validate element target
if (type !== 'primitive' && !target)
throw new Error(
`R3F: ${name} is not part of the THREE namespace! Did you forget to extend? See: https://docs.pmnd.rs/react-three-fiber/api/objects#using-3rd-party-objects-declaratively`,
)
// Validate primitives
if (type === 'primitive' && !props.object) throw new Error(`R3F: Primitives without 'object' are invalid!`)
// Throw if an object or literal was passed for args
if (props.args !== undefined && !Array.isArray(props.args)) throw new Error('R3F: The args prop must be an array!')
}
function createInstance(type: string, props: HostConfig['props'], root: RootStore): HostConfig['instance'] {
// Remove three* prefix from elements if native element not present
type = toPascalCase(type) in catalogue ? type : type.replace(PREFIX_REGEX, '')
validateInstance(type, props)
// Regenerate the R3F instance for primitives to simulate a new object
if (type === 'primitive' && props.object?.__r3f) delete props.object.__r3fView on GitHub (pinned to ff3899dbf4)
Solutions
- If it's a typo, correct the tag name so its PascalCase form matches a THREE export (e.g. mesh -> THREE.Mesh).
- For third-party or addon objects, register them: extend({ OrbitControls }) (and import OrbitControls from 'three/examples/jsm/controls/OrbitControls.js' or 'three-stdlib') before using <orbitControls />.
- Make sure the class is actually imported so it's included in the bundle — add an explicit import even if only used in extend().
- After upgrading three/R3F, re-check the addon import path and re-run extend() with the new exports.
Example fix
// before
import { Canvas } from '@react-three/fiber'
<Canvas><orbitControls /></Canvas> // R3F: OrbitControls is not part of the THREE namespace!
// after
import { Canvas, extend } from '@react-three/fiber'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
extend({ OrbitControls })
<Canvas><orbitControls /></Canvas> Defensive patterns
Strategy: validation
Validate before calling
import * as THREE from 'three'
import { extend } from '@react-three/fiber'
// Before rendering third-party elements, verify registration
export function ensureRegistered(name: string, ctor: unknown) {
if (!(name in (THREE as any))) extend({ [name]: ctor as any })
}
ensureRegistered('OrbitControls', OrbitControls) Type guard
import * as THREE from 'three'
const isThreeClass = (name: string): boolean => {
const pascal = name.charAt(0).toUpperCase() + name.slice(1)
return pascal in THREE || ['primitive'].includes(name)
} Try / catch
null
Prevention
- Call extend() once at module scope, near the imports of the addon classes.
- Explicitly import every class you reference via JSX so bundlers can't tree-shake it.
- After upgrading three/R3F, smoke-test each custom JSX tag (typos fail only at render time).
When it happens
Trigger: Using a JSX tag whose PascalCase name is not in the catalogue: typos like <meesh>, third-party/derived classes used without calling extend({ OrbitControls }), tree-shaken/custom builds where the class isn't imported from 'three', or namespaced usage like <threeMesh> when the underlying THREE.Mesh is not exported by the installed three version.
Common situations: Upgrading three.js or R3F versions where an addon moved from examples/jsm to a different path; using controls/postprocessing from three-stdlib or drei imports without extend(); a typo'd element name; bundler tree-shaking removing a class that was never explicitly imported; JSX intrinsic types present but runtime registration missing.
Related errors
- `${prefix} ${msg}`
- Timed out after ${timeout}ms.
- R3F: Hooks can only be used within the Canvas component!
- R3F: Primitives without 'object' are invalid!
- R3F: The args prop must be an array!
AI-assisted analysis of pmndrs/react-three-fiber@ff3899dbf4 (2026-08-28).
Data as JSON: /api/errors/4ca47a6f9bbd220f.
Report an issue: GitHub.