pmndrs/react-three-fiber · error · Error

R3F: Primitives without 'object' are invalid!

Error message

R3F: Primitives without 'object' are invalid!

What it means

In R3F, <primitive> is a special element type that wraps an existing, already-constructed THREE.Object3D (or similar) instead of instantiating a class from the catalogue. Because there is nothing to construct, the object prop is mandatory, and the reconciler's validateInstance throws this error during createInstance/commitUpdate if it is missing. The check prevents primitive from silently rendering an empty/invalid node.

Source

Thrown at packages/fiber/src/core/reconciler.tsx:184

    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

  return prepare(props.object, root, type, props)
}

function hideInstance(instance: HostConfig['instance']): void {

View on GitHub (pinned to ff3899dbf4)

Solutions

  1. Ensure the object prop is always provided: <primitive object={myObject3D} />.
  2. If the object may be undefined initially (async load), gate rendering: {obj && <primitive object={obj} />} or suspend behind Suspense via useLoader so the object exists when the subtree renders.
  3. Check for prop typos — the prop must be named exactly 'object'.
  4. If the object can be recreated, pass key={obj.uuid} (or key={obj.id}) so React remounts the primitive when the underlying object identity changes, since primitive props are applied as diff, not constructor args.

Example fix

// before
{data && data.nodes && <primitive object={data.nodes[10]} />}
<primitive object={obj.model} /> // obj.model is undefined while loading

// after
{obj.model && <primitive object={obj.model} key={obj.model.uuid} />}
// or suspend until loaded:
const { nodes } = useLoader(GLTFLoader, '/model.glb')
<primitive object={nodes.MyMesh} />
Defensive patterns

Strategy: validation

Validate before calling

const obj: THREE.Object3D | undefined = loadedModel?.scene
return (
  {obj ? <primitive object={obj} key={obj.uuid} /> : <FallbackPlaceholder />}
)

Type guard

const isObject3D = (v: unknown): v is THREE.Object3D =>
  !!v && typeof v === 'object' && (v as any).isObject3D === true

Try / catch

null

Prevention

When it happens

Trigger: Rendering <primitive /> with no object prop; passing object={undefined} or object={null} (e.g. an object that is still loading, or a memoized value that hasn't been created yet); spreading props where the object key is absent or misspelled (<primitive obj={...} />).

Common situations: Rendering a lazily created or async-loaded object before it exists (object={maybeObject} where maybeObject is undefined initially); refactoring from useLoader where the loaded asset can be undefined on first render; a typo'd prop name (obj vs object); HMR losing the memoized object reference.

Related errors


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