Skip to content

How It Works

Fresh

React Three Fiber is a React renderer for three.js.

This means that each Fiber component will effectively create a new THREE object that will be added to a scene. Understanding this is not necessarily needed to use Fiber, but it will better arm you to deal with anything that you might need in your projects, read other people's Fiber code, and even help you contribute.

JSX to three.js

jsx
import { Canvas } from '@react-three/fiber'

function MyApp() {
  return (
    <Canvas>
      <group>
        <mesh>
          <meshNormalMaterial />
          <boxGeometry args={[2, 2, 2]} />
        </mesh>
      </group>
    </Canvas>
  )
}

In three.js, this is equivalent to:

js
import * as THREE from 'three'

const scene = new THREE.Scene() // <Canvas>

const group = new THREE.Group() // <group>

const mesh = new THREE.Mesh() // <mesh />
const material = new THREE.MeshNormalMaterial() // <meshNormalMaterial />
const geometry = new THREE.BoxGeometry(2, 2, 2) // <boxGeometry />

mesh.material = material
mesh.geometry = geometry

group.add(mesh)
scene.add(group)

Our Canvas element will create a new scene, and Fiber will instantiate new objects for each component and correctly compose them together in a scene graph.

Additionally, Fiber will:

  • Set up a new perspective camera at [0, 0, 0] and set it as default
  • Set up a render loop with automatic render to screen
  • Set up pointer events via raycasting on all meshes with onPointer props
  • Set up tone mapping
  • Automatically handle window resize

Creating THREE Objects

In three.js, we can create new objects using the classic JS API:

js
const myBox = new THREE.BoxGeometry(1, 2, 3)

Object creation is handled transparently by the Fiber renderer. The name of the constructor BoxGeometry is equivalent to the camel case component <boxGeometry />, while the constructor arguments are passed via the args prop:

jsx
<boxGeometry args={[1, 2, 3]} />

INFO

The object will be created only when first adding the component to the React tree.

The attach Prop

Fiber always tries to correctly infer the relationship between components and their parents. For groups:

jsx
<group>
  <mesh />
</group>

Fiber knows that a group can only have children, so it calls group.add(mesh).

For meshes, rules can be different. A THREE.Mesh object is constructed using a material and a geometry. With the attach prop, we can precisely tell the renderer what property to attach each component to:

jsx
<mesh>
  <meshNormalMaterial attach="material" />
  <boxGeometry attach="geometry" />
</mesh>

This tells Fiber to render like this:

js
mesh.material = new THREE.MeshNormalMaterial()
mesh.geometry = new THREE.BoxGeometry()

Fiber also infers the attach property from the constructor name, so anything with material or geometry in the name will automatically attach to the correct property.

Props

With Fiber, you can pass any three.js property as a React prop, and it will be assigned to the constructed object:

jsx
<meshBasicMaterial color="red" />

Equivalent to:

js
const material = new THREE.MeshBasicMaterial()
material.color = 'red'

Fiber will check the type of the property value and either:

  • Assign the new value directly
  • If the value is an object with a .set() method, call that
  • Construct a new object if needed
  • Convert between formats
jsx
<mesh scale={[1, 2, 3]} />

Equivalent to:

js
const mesh = new THREE.Mesh()
mesh.scale = new THREE.Vector3(1, 2, 3)

// on update, it will instead set() the vector
mesh.scale.set(3, 4, 5)

Pointer Events

Pointer Events are transparently handled by Fiber. On startup, it will create a raycaster for mouse picking.

Every object with onPointer props will be added to the array of objects checked every frame:

jsx
<mesh onPointerDown={console.log}>...</mesh>

The ray's origin and direction are updated every time the mouse moves on the <Canvas /> element or the window is resized. Fiber also handles camera switching, meaning that the raycaster will always use the currently active camera.

When using the raycast prop, the object will instead be picked using a custom ray:

jsx
import { useCamera } from '@react-three/drei'

return <mesh raycast={useCamera(anotherCamera)} />

Render Loop

By default, Fiber will set up a render loop that renders the default scene from the default camera to a WebGLRenderer.

The loop is set up using setAnimationLoop, which will execute its callback every time a new frame is renderable. This is what will happen every render:

  1. All global before effects are executed
  2. Clock delta is saved — implying all useFrame calls will share the same delta
  3. useFrame callbacks are executed in order
  4. renderer.render(scene, camera) is called, rendering the scene to screen
  5. All global after effects are executed