Command Palette

Search for a command to run...

Hectal
Course outline
Topic 11.1Intermediate ~19 min · step 1 of 7

Why React Three Fiber

In one line

R3F writes the same three.js objects as JSX and keeps them in step with React state. It does not wrap or hide three.js: every tag is a real three.js class, and the version rule is fiber 9 with React 19.

0/7 · 0%

Think of it like this

An architect's drawing and a builder. You hand the builder a drawing ("a wall here, a window there") and say what changed in the new version. The builder works out what to build, move or knock down. JSX is the drawing, R3F is the builder, and three.js is the building.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

renderer (React)
A program that takes React components and builds something other than web page elements from them. react-dom builds HTML; R3F builds three.js objects.
JSX
The tag-like syntax React uses to describe what to build, such as <mesh />. A build tool turns it into plain function calls.
Canvas
R3F's root component. It creates the renderer, the scene and the camera, and everything inside it becomes three.js objects.
args
A prop holding the list of arguments for a class's constructor. args={[1, 2, 3]} is new Box(1, 2, 3).
drei
The helper library for R3F (German for "three"). It holds ready-made controls, loaders and effects.

Step by step

01One tag, one class

<mesh name="cube"> creates a THREE.Mesh and sets mesh.name = "cube". Its two children attach themselves to it: the geometry tag fills mesh.geometry, the material tag fills mesh.material. Nothing is drawn yet.

02The Canvas builds the world

<Canvas> makes a WebGLRenderer, a Scene and a PerspectiveCamera, and starts a loop. Its camera prop is applied to the default camera (position: [0, 0, 3]). onCreated runs once the world exists and gives you scene, gl and camera.

03Hooks reach the live objects

useFrame((state, delta) => ...) runs a function before each draw. ref.current is the real THREE.Mesh, so ref.current.rotation.y += delta is exactly the three.js you already know.

04Read the defaults

The second example prints what <Canvas> chose. The flat prop turns tone mapping off. Compare the two balls: the same material gets a different brightness and saturation, because tone mapping remaps bright colours (Topic 5.6).

Try it yourself

  1. 1

    Add a second mesh

    In the first example, add <mesh position-x={1}><boxGeometry /></mesh> next to <Cube />. The output becomes objects in the scene: 2 | Mesh cube. The new mesh has no material tag, so three.js gives it a default one.

  2. 2

    Remove flat

    In the second example, delete flat. The right ball now matches the left one, and the second report line says ACESFilmicToneMapping. R3F sets tone mapping unless you pass flat or set gl={{ toneMapping }} yourself (the build topic does).

Code & diagrams

The turning cube, as JSX New tab

The first line is R3F's own clock warning (explained above). The scene holds one object: the Cube component's mesh.

idle

Console

Run the scene to see its console.

Expected console output

THREE.Clock: This module has been deprecated. Please use THREE.Timer instead.
objects in the scene: 1 | Mesh cube
What <Canvas> chose for you New tab

Two Canvases, so the warning appears twice. Left: the defaults. Right: `flat`. Compare the colour of the two balls.

idle

Console

Run the scene to see its console.

Expected console output

THREE.Clock: This module has been deprecated. Please use THREE.Timer instead.
default: WebGLRenderer | camera PerspectiveCamera fov 75 at 0,0,5
default: tone mapping ACESFilmicToneMapping | output srgb | scene.background null
THREE.Clock: This module has been deprecated. Please use THREE.Timer instead.
flat: WebGLRenderer | camera PerspectiveCamera fov 75 at 0,0,5
flat: tone mapping NoToneMapping | output srgb | scene.background null

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Forget the Canvas

Render <mesh><boxGeometry /></mesh> straight into createRoot(...), with no <Canvas>.

terminal
$ Run
── what you'll see ──
<boxGeometry /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.
The tag <boxGeometry> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.
The tag <mesh> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.

Myth vs fact

Myth

R3F is a different engine, so it renders differently or slower.

Fact

It calls the same three.js. The numbers in the status bar are the same as for hand-written code. Only React's own work (re-rendering components) is extra.

Myth

You have to learn new names for everything.

Fact

The names are three.js class names with a lowercase first letter, and the props are the class's properties. If you know MeshStandardMaterial({ roughness }), you know <meshStandardMaterial roughness={...} />.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    R3F is a custom React reconciler (the same mechanism as react-dom). When state changes, React works out which tags changed and R3F applies only those changes to the three.js objects. It never rebuilds the scene.

  • ▸

    Canvas gives every R3F tree its own React root and its own WebGL context. Two Canvases are two contexts (Chromium keeps at most 16 per page), and React context from outside the Canvas does not cross into it. Re-provide any context you need inside.

  • ▸

    The default dpr is [1, 2]: pixel ratio is the device's, but never above 2. On a phone at ratio 3 that already saves more than half the pixels (Topic 1.1).

Remember this

  1. 1

    The problem this solves: a page has buttons, lists and prices (UI) and a 3D scene. In plain three.js the two live in different worlds: you write listeners that reach into the scene, and keep state in both. In R3F the scene is part of the component tree, so one piece of state can drive both.

  2. 2

    What a tag is. <mesh> means new THREE.Mesh(). <boxGeometry args={[1, 1, 1]} /> means new THREE.BoxGeometry(1, 1, 1), attached to its parent as geometry. The name is the class name with a lowercase first letter. R3F reads the list of classes from the installed three.js, so every class is available, including ones you write yourself.

  3. 3

    What R3F does for you. It makes the renderer, the scene and a camera, sizes the canvas to its parent, sets the pixel ratio (between 1 and 2 by default), runs the render loop, finds clicks with a raycaster (Phase 8), and creates and dispose()s objects as components mount and unmount. In the second example you can read the defaults it chose: a perspective camera with field of view 75 at z = 5, ACES tone mapping and sRGB output.

  4. 4

    What R3F does not do. It is not a new engine and it is not slower by itself: the draw calls, triangles and memory are exactly the same as the equivalent three.js. Geometry cost, material count and texture size (Phase 17) are still your job. The only extra cost is React's work when state changes, which Topic 11.3 shows how to avoid.

  5. 5

    The version rule. @react-three/fiber 9 needs React 19 (fiber 8 is for React 18). The course pins @react-three/fiber 9.8.2, @react-three/drei 10.7.9, react 19.2.8 and three 0.186.1. Mismatched versions are the most common cause of strange install errors, so pin them as the build topic does.

  6. 6

    When plain three.js is the better choice. A page with no React, a scene with almost no UI, or code that must run in a worker or a non-React app: use three.js directly. R3F also does not help with a tight custom render loop. For a product page that already uses React, it usually removes code.

  7. 7

    The Clock warning. With three r186, R3F 9.8.2 prints THREE.Clock: This module has been deprecated. Please use THREE.Timer instead. once for every Canvas. It comes from R3F's own clock, not from your code. Nothing is broken, and you cannot silence it by editing your scene. It will go away in a later R3F release that uses THREE.Timer; until then, expect to see it as the first line in every R3F example on this page.

Explain it without notes

01

What does <mesh><boxGeometry args={[1, 1, 1]} /></mesh> create, and how do the two tags find each other?

02

A teammate says R3F will make the 3D scene slow because it adds a layer. What is true and what is not?

Practice

01

Write the JSX for a mesh named wheel at x = 0.58, y = 0.35: a TorusGeometry with radius 0.33 and tube 0.022, in dark grey #22262c rubber with roughness 0.9.

02

Install the packages for an R3F project with exact versions, and say which two version numbers must go together.

Trade-offs

  • ↔

    R3F is shorter and shares state with the UI, but it adds React and the reconciler to your bundle, and a team must know React well. For a page with no React, plain three.js is simpler.

Done when you can

  • Say what a JSX tag creates and how a child tag attaches to its parent

  • Say what Canvas builds for you and read its defaults

  • State the version rule: fiber 9 with React 19

  • Explain the Clock warning and why it is harmless