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.__r3f

View on GitHub (pinned to ff3899dbf4)

Solutions

  1. If it's a typo, correct the tag name so its PascalCase form matches a THREE export (e.g. mesh -> THREE.Mesh).
  2. 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 />.
  3. Make sure the class is actually imported so it's included in the bundle — add an explicit import even if only used in extend().
  4. 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

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


AI-assisted analysis of pmndrs/react-three-fiber@ff3899dbf4 (2026-08-28). Data as JSON: /api/errors/4ca47a6f9bbd220f. Report an issue: GitHub.