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
- Ensure the object prop is always provided: <primitive object={myObject3D} />.
- 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.
- Check for prop typos — the prop must be named exactly 'object'.
- 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
- Always conditionally render primitives: {obj && <primitive object={obj} />}.
- Use Suspense + useLoader so loaded assets are guaranteed to exist at render time.
- Remember the prop name is exactly 'object' — TypeScript JSX types help catch typos.
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
- R3F: The args prop must be an array!
- `${prefix} ${msg}`
- Timed out after ${timeout}ms.
- R3F: Hooks can only be used within the Canvas component!
- R3F: ${name} is not part of the THREE namespace! Did you for
AI-assisted analysis of pmndrs/react-three-fiber@ff3899dbf4 (2026-08-28).
Data as JSON: /api/errors/4f65ccba9a89af22.
Report an issue: GitHub.