Appearance
Objects
FreshYou can use three.js's entire object catalogue and all properties. When in doubt, always consult the three.js docs.
Declaring Objects
Avoid creating objects imperatively — it forces re-creation on every render:
jsx
// AVOID — these properties will always be re-created
<mesh
visible
userData={{ hello: 'world' }}
position={new THREE.Vector3(1, 2, 3)}
rotation={new THREE.Euler(Math.PI / 2, 0, 0)}
geometry={new THREE.SphereGeometry(1, 16, 16)}
material={new THREE.MeshBasicMaterial({ color: new THREE.Color('hotpink'), transparent: true })}
/>Instead, define properties declaratively:
jsx
// PREFER — declarative props
<mesh visible userData={{ hello: 'world' }} position={[1, 2, 3]} rotation={[Math.PI / 2, 0, 0]}>
<sphereGeometry args={[1, 16, 16]} />
<meshStandardMaterial color="hotpink" transparent />
</mesh>Constructor Arguments
In three.js, objects are classes that are instantiated. Constructor arguments are passed as an array via args. If args change later on, the object will be reconstructed from scratch.
jsx
<sphereGeometry args={[1, 32]} />Shortcuts
Set
All properties whose underlying object has a .set() method can directly receive the same arguments that set would take.
THREE.Color.setcan take a color string, socolor="hotpink"worksTHREE.Vector3takes multiple arguments, so use an array:position={[100, 0, 0]}
jsx
<mesh position={[1, 2, 3]} />
<meshStandardMaterial color="hotpink" />INFO
If you link up an existing object to a property like a THREE.Vector3() to position, R3F will copy the object in most cases by calling .copy() on the target. If you link a material or geometry, it will overwrite because these objects do not have a .set() method.
SetScalar
Properties that have a setScalar method (like Vector3) can be set with a single number:
jsx
// Translates to <mesh scale={[1, 1, 1]} />
<mesh scale={1} />Piercing into Nested Properties
Use dash-case to reach into nested attributes:
jsx
<mesh rotation-x={1} material-uniforms-resolution-value={[512, 512]} />Dealing with Non-Scene Objects
You can put non-Object3D primitives (geometries, materials, etc.) into the render tree. They take the same properties and constructor arguments they normally would. They are managed, reactive and auto-dispose. These objects are not technically part of the scene, but they "attach" to a parent which is.
Attach
Use attach to bind objects to their parent. If you unmount the attached object it will be taken off its parent automatically.
jsx
<mesh>
<meshBasicMaterial attach="material" />
<boxGeometry attach="geometry" />INFO
All objects extending THREE.Material receive attach="material", and all objects extending THREE.BufferGeometry receive attach="geometry" automatically. You do not have to type it out!
jsx
<mesh>
<meshBasicMaterial />
<boxGeometry />Deeply Nested Attach
Attach through piercing adds a buffer-attribute to geometry.attributes.position:
jsx
<mesh>
<bufferGeometry>
<bufferAttribute attach="attributes-position" args={[v, 3]} />More Examples
jsx
// Attach bar to foo.a
<foo>
<bar attach="a" />
// Attach bar to foo.a.b and foo.a.b.c (nested object attach)
<foo>
<bar attach="a-b" />
<bar attach="a-b-c" />
// Attach bar to foo.a[0] and foo.a[1] (array attach is just object attach)
<foo>
<bar attach="a-0" />
<bar attach="a-1" />
// Attach bar to foo via explicit add/remove functions
<foo>
<bar attach={(parent, self) => {
parent.add(self)
return () => parent.remove(self)
}} />
// The same as a one liner
<foo>
<bar attach={(parent, self) => (parent.add(self), () => parent.remove(self))} />Attaching a Shadow Camera
diff
- <directionalLight
- castShadow
- position={[2.5, 8, 5]}
- shadow-mapSize={[1024, 1024]}
- shadow-camera-far={50}
- shadow-camera-left={-10}
- shadow-camera-right={10}
- shadow-camera-top={10}
- shadow-camera-bottom={-10}
- />
+ <directionalLight castShadow position={[2.5, 8, 5]} shadow-mapSize={[1024, 1024]}>
+ <orthographicCamera attach="shadow-camera" args={[-10, 10, 10, -10]} />
+ </directionalLight>Multi-Materials (Arrays)
Arrays must have explicit order:
jsx
<mesh>
{colors.map((color, index) => <meshBasicMaterial key={index} attach={`material-${index}`} color={color} />}
</mesh>Primitives
Use the primitive placeholder to put existing objects into the scene graph. Primitives will not dispose of the object they carry on unmount — you are responsible for disposing.
jsx
const mesh = new THREE.Mesh(geometry, material)
function Component() {
return <primitive object={mesh} position={[10, 0, 0]} />WARNING
Scene objects can only ever be added once in three.js. If you want to re-use an existing object, you must clone it first.
Using 3rd-Party Objects
The extend function extends R3F's catalogue of JSX elements:
jsx
import { extend } from '@react-three/fiber'
import { OrbitControls, TransformControls } from 'three-stdlib'
extend({ OrbitControls, TransformControls })
// ...
return (
<>
<orbitControls />
<transformControls />If using TypeScript, you'll also need to extend the JSX namespace.
Disposal
Freeing resources is a manual chore in three.js, but React Three Fiber will attempt to free resources for you by calling object.dispose() on all unmounted objects.
If you manage assets by yourself, disable automatic disposal with dispose={null}:
jsx
const globalGeometry = new THREE.BoxGeometry()
const globalMaterial = new THREE.MeshBasicMaterial()
function Mesh() {
return (
<group dispose={null}>
<mesh geometry={globalGeometry} material={globalMaterial} />Shader Material Uniforms
ShaderMaterial, RawShaderMaterial and subclasses keep the material's uniforms object stable. R3F merges incoming uniforms into the existing target instead of replacing it:
jsx
<shaderMaterial
uniforms={{
time: { value: time },
color: { value: color },
}}
/>For stable reference to uniforms, use a React ref:
jsx
function Component() {
const ref = useRef()
const uniforms = useMemo(
() => ({
time: { value: 0 },
color: { value: color },
}),
[color],
)
useFrame(({ clock }) => {
// Mutate the stable uniforms object directly
ref.current.uniforms.time.value = clock.elapsedTime
})
return (
<mesh>
<shaderMaterial ref={ref} uniforms={uniforms} />
</mesh>
)
}