Three.js is the 3d animation library which is used to design games, movies, scenes, animations, graphics, vfx etc. If you want to learn more about three.js then follow our introductory guide –

In this article, we will learn about react-three-fiber which is a react renderer for three.js. According to official page

React-three-fiber is required for react developers because you can build your scene declaratively with re-usable, self-contained components that react to state, are readily interactive and can tap into React’s ecosystem.

Introduction

React-three-fiber is the react renderer for three.js. It means that all the functionality of three.js is supported in this library. It doesn’t depend on any particular version of three.js. So, overall there are no limitations and no performance lag.

To install react-three-fiber, use npm –

npm install three @react-three/fiber

You should create your project using create-react-app.

Prerequisite

  1. You should have a basic understanding of three.js. You may learn the introduction and fundamentals from this guide.
  2. Be aware of React coding. This whole blog is filled with react articles, but you may refer official documentation.

Three.js Vs React-three-fiber

In this section, we will look at the differences between three.js and React-three-fiber. We need to understand how something in three.js could be done in react-three-fiber.

As we have discussed in our three.js guide, there are three main entities for creating an animation –

  1. Scene
  2. Camera
  3. Renderer

Let’s see three.js code first and then we will check out the equivalent react-three-fiber code –

<script>
  const scene = new THREE.Scene();

  const geometry = new THREE.BoxGeometry();
  const material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
  const cube = new THREE.Mesh( geometry, material );

  scene.add( cube );

  const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
  camera.position.z = 5;

  const renderer = new THREE.WebGLRenderer();
  renderer.setSize( window.innerWidth, window.innerHeight );
  document.body.appendChild( renderer.domElement );

  const animate = function () {
    requestAnimationFrame( animate );

    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;

    renderer.render( scene, camera );
  };

  animate();
</script>

Here we have scene, box geometry, camera, renderer and animation loop.

In react-three-fiber, we have Canvas, mesh and useFrame() hook. Let’s see their functions and how they are used –

<Canvas>

<Canvas> is the component provided by react-three-fiber which handles the scene, camera and auto re-rendering. So, a single <Canvas> code could handle this much of three.js code –

<script>
  const scene = new THREE.Scene();

  // const geometry = new THREE.BoxGeometry();
  // const material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
  // const cube = new THREE.Mesh( geometry, material );

  // scene.add( cube );

  const camera = new THREE.PerspectiveCamera( 75, parentDivWidth / parentDivHeight, 0.1, 1000 );
  // camera.position.z = 5;

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

  const animate = function () {
    requestAnimationFrame( animate );

    // cube.rotation.x += 0.01;
    // cube.rotation.y += 0.01;

    renderer.render( scene, camera );
  };

  animate();
</script>

Here is the equivalent react-three-fiber code –

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

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

If you want to change the width or height of canvas, then change the size of parent container, i.e. #canvas-container. The Canvas will automatically resize.

Since Canvas component handles both scene and camera, so there are few props which could come handy in setting properties like camera fov, aspect ratio, far clip etc. This below table will help you in setting Canvas options –

PROPDESCRIPTIONDEFAULT
childrenThreejs jsx elements or regular components
glProps that go into the default renderer, or your own renderer{}
cameraProps that go into the default camera, or your own THREE.Camera{ fov: 75, near: 0.1, far: 1000, position: [0, 0, 5] }
shadowsProps that go into gl.shadowMap, can also be set true for PCFsoftfalse
raycasterProps that go into the default raycaster{}
vrSwitches renderer to VR mode, then uses gl.setAnimationLoopfalse
modeReact mode: legacy, blocking, concurrentblocking
resizeResize config, see react-use-measure’s options{ scroll: true, debounce: { scroll: 50, resize: 0 } }
orthographicCreates an orthographic camerafalse
dprPixel-ratio, use window.devicePixelRatio, or automatic: [min, max]undefined
linearSwitch off automatic sRGB encoding and gamma correctionfalse
flatUse THREE.NoToneMapping instead of THREE.ACESFilmicToneMappingfalse
onCreatedCallback after the canvas has rendered (but not yet committed)(state) => {}
onPointerMissedResponse for pointer clicks that have missed any target(event) => {}

<mesh>

This component holds the geometries and materials. Like in three.js, we create geometry, material and a mesh, here we have components for all of them. Suppose our three.js code is –

const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
const cube = new THREE.Mesh( geometry, material );

The equivalent code in react-three-fiber is –

<mesh>
  <boxGeometry />
  <meshBasicMaterial color={"#ff0000"} />
</mesh>
  • THREE.Mesh is represented by <mesh> component.
  • Three.BoxGeometry is represented by <boxGeometry /> component.
  • Three.MeshBasicMaterial is represented by <meshBasicMaterial /> component.

To include the mesh in the scene, three.js code is –

scene.add( cube );

But in react library, we put the <mesh> component as child of <Canvas> component.

<Canvas>
  <mesh>
    <boxGeometry />
    <meshBasicMaterial color={"#ff0000"} />
  </mesh>
</Canvas>

You can have any number of mesh objects within your canvas.

useFrame()

To run an animation loop, three.js uses requestAnimationFrame function. The same could be achieved using useFrame() hook in react-three-fiber. Note: Fiber hooks can only be called inside a <Canvas> parent so you need to call it from a separate component.

import React from 'react';
import { Canvas, useFrame } from '@react-three/fiber'

const AnimateFrame = (props) => {
  useFrame(({ clock }) => {
    props.meshRef.current.rotation.x += 0.01;
  });
  return null;
}

export default function App() {

  const myMesh = React.useRef();

  return (
    <div id="canvas-container">
      <Canvas>
        <mesh ref={myMesh}>
          <boxGeometry />
          <meshBasicMaterial color={"#ff0000"} />
        </mesh>
        <AnimateFrame meshRef={myMesh} />
      </Canvas>
    </div>
  )
}

Here we have created a ref variable, myMesh which is passed as ref prop in <mesh>. To play animation using useFrame(), we created a different component, AnimateFrame. Then, we included this component as a child of <Canvas> and passed myMesh as prop because we want to rotate mesh object and for that we need our <mesh> reference in AnimateFrame.

    Tweet this to help others

Live Demo

Comments

About the Author

akamit

I am Akash Mittal, an overall computer scientist. If you want to guest post, need help in your projects, want to advertise, Feel free to contact me at [email protected]

View All Articles