Skip to content

Your First Scene

Fresh

This tutorial will assume some React knowledge.

Setting Up the Canvas

We'll start by importing the <Canvas /> component from @react-three/fiber and putting it in our React tree.

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

function App() {
  return (
    <div id="canvas-container">
      <Canvas />
    </div>
  )
}

createRoot(document.getElementById('root')).render(<App />)

The Canvas component does some important setup work behind the scenes:

  • It sets up a Scene and a Camera, the basic building blocks necessary for rendering
  • It renders our scene every frame — you do not need a traditional render-loop

TIP

Canvas is responsive to fit the parent node, so you can control how big it is by changing the parent's width and height, in this case #canvas-container.

Adding a Mesh Component

To actually see something in our scene, we'll add a lowercase <mesh /> native element, which is the direct equivalent to new THREE.Mesh().

jsx
<Canvas>
  <mesh />

INFO

Note that we don't need to import anything — all three.js objects will be treated as native JSX elements, just like you can write <div /> or <span /> in regular ReactDOM. The general rule is that Fiber components are available under the camel-case version of their name in three.js.

A Mesh is a basic scene object in three.js, and it's used to hold the geometry and the material needed to represent a shape in 3D space. We'll create a new mesh using a BoxGeometry and a MeshStandardMaterial which automatically attach to their parent.

jsx
<Canvas>
  <mesh>
    <boxGeometry />
    <meshStandardMaterial />
  </mesh>
</Canvas>

This is equivalent to the following three.js code:

js
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)

const renderer = new THREE.WebGLRenderer()
renderer.setSize(width, height)
document.querySelector('#canvas-container').appendChild(renderer.domElement)

const mesh = new THREE.Mesh()
mesh.geometry = new THREE.BoxGeometry()
mesh.material = new THREE.MeshStandardMaterial()

scene.add(mesh)

function animate() {
  requestAnimationFrame(animate)
  renderer.render(scene, camera)
}

animate()

Constructor Arguments

According to the docs for BoxGeometry we can optionally pass three arguments for: width, length and depth:

js
new THREE.BoxGeometry(2, 2, 2)

In Fiber we use the args prop, which always takes an array whose items represent the constructor arguments.

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

WARNING

Every time you change args, the object must be re-constructed!

Adding Lights

Next, we will add some lights to our scene:

jsx
<Canvas>
  <ambientLight intensity={0.1} />
  <directionalLight color="red" position={[0, 0, 5]} />

Props

This introduces us to the last fundamental concept of Fiber — how React props work on three.js objects. When you set any prop on a Fiber component, it will set the property of the same name on the three.js instance.

jsx
<ambientLight intensity={0.1} />

Which is the equivalent to:

jsx
const light = new THREE.AmbientLight()
light.intensity = 0.1

Shortcuts

There are a few shortcuts for props that have a .set() method (colors, vectors, etc).

jsx
const light = new THREE.DirectionalLight()
light.position.set(0, 0, 5)
light.color.set('red')

Which is the same as:

jsx
<directionalLight position={[0, 0, 5]} color="red" />

The Result

Putting it all together:

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

export default function App() {
  return (
    <Canvas>
      <mesh>
        <boxGeometry args={[2, 2, 2]} />
        <meshPhongMaterial />
      </mesh>
      <ambientLight intensity={0.1} />
      <directionalLight position={[0, 0, 5]} color="red" />
    </Canvas>
  );
}

Things to try

  • Try different materials like MeshNormalMaterial or MeshBasicMaterial, give them a color
  • Try different geometries like SphereGeometry or OctahedronGeometry
  • Try changing the position on our mesh component
  • Try extracting our mesh to a new component