Decoratable prebuilds & slots
Use optional composed set-pieces, toggle named decor, and attach your own models to placement slots.
Some packs include prebuilds: composed set-pieces that can save scene-layout time. A prebuild may expose a base, named decor objects, and empty placement slots. This structure is optional, and names differ by release; inspect the downloaded component, GLB, and README before addressing a node in code.
Inspect the hierarchy#
A decoratable prebuild can look like this:
<prebuild>
├── base
├── decor
│ ├── sign
│ └── lantern
└── slots
└── slot_flagThe names above are illustrative. base and decor are stable shapes, but slot names are chosen per set-piece: slot_flag, slot_bench, slot_label, slot_cart, slot_tree, and slot_lantern are common, and a pack can use a name that appears nowhere else. Read the names out of your own file.
Do not rely on that exact hierarchy for every prebuild. Some set-pieces expose fewer editable objects, and some included components render the whole scene through a clone rather than individual typed meshes.
Prebuilds may be Draco-compressed. If the README identifies a compressed file, self-host the decoder and pass its path to your loader.
Show or hide decor#
Clone the loaded scene before changing visibility so another instance is not changed through the loader cache:
import { useEffect, useMemo } from 'react'
import { useGLTF } from '@react-three/drei'
export function Prebuild({ showSign }: { showSign: boolean }) {
const { scene } = useGLTF('/prebuilds/street_corner.glb', '/draco/')
const instance = useMemo(() => scene.clone(true), [scene])
useEffect(() => {
const sign = instance.getObjectByName('sign')
if (sign) sign.visible = showSign
}, [instance, showSign])
return <primitive object={instance} />
}The URL and the object name above are placeholders. Use the exact path and name from your own download. If an object is absent, decide whether to skip the customization or treat it as a release-compatibility error.
Attach a model to a slot#
A slot is an empty Object3D whose transform defines a placement point. Reach it through the component's typed nodes when you can, and fall back to getObjectByName when you cannot.
Included prebuild components declare their slots in the generated nodes type as slot_<name>: THREE.Object3D. Addressing a slot through nodes makes a renamed or removed slot a compile error in your editor instead of a silent undefined at runtime:
import { createPortal } from '@react-three/fiber'
import { useGLTF } from '@react-three/drei'
const { nodes } = useGLTF('/prebuilds/plaza.glb') as GLTFResult
return (
<>
<primitive object={nodes.base} />
{createPortal(<YourVehicle />, nodes.slot_flag)}
</>
)GLTFResult is the type the included component declares for that file; copy it from the component or import it if the component exports it.
Use getObjectByName when the typed nodes do not apply: on a scene you cloned yourself, on a pack whose component clones rather than exposing typed meshes, or when you load the GLB without its component. Those lookups return Object3D | undefined, so keep the guard:
const slot = instance.getObjectByName('slot_flag')
return (
<>
<primitive object={instance} />
{slot ? createPortal(<YourVehicle />, slot) : null}
</>
)nodes refers to the shared cached scene, so a slot you read from it is not the same object as the matching slot on a clone. Pick one access path per instance.
The attached model keeps its own local pivot, so you may still need a small position or rotation adjustment. Slot names and intended contents belong in the pack README or component types.
Update safely#
When replacing a pack release, test every node or slot name used by your code. Keep the previous download until the updated set-piece has passed visual and interaction checks.
Related#
- Using a pack in React Three Fiber — load files and configure Draco.
- Scale, orientation & pivots — understand local origins and placement.
- The showroom & demo world — distinguish optional inspection tools from the pack files.
Ask us directly — we answer support mail ourselves.