About The Author

Fortune Ikechi is a Frontend Engineer based in Rivers State Nigeria. He is a student of the University of Port-Harcourt. He is passionate about community and … More about Fortune

A Dive Into React And Three.js Using react-three-fiber

        react-three-fiber is a powerful Three.js renderer that helps render 3D models and animations for React and its native applications. In this tutorial, you will learn how to configure and build 3D models in a React application.

        Today, we’re going to learn how to configure and use react-three-fiber for building and displaying 3D models and animations in React and React Native applications.

        This tutorial is for developers who want to learn more about 3D model animations in the web using React and for anyone who’s had limitations with Three.js like inability to create canvas, bind user events like click events and start a render loop, react-three-fiber comes with these methods. We’ll be building a 3D model to better understand how to build Three.js 3D models using react-three-fiber.

        Getting Started With react-three-fiber

        Three.js is a library that makes it easier to create 3D graphics in the browser, it uses a canvas + WebGL to display the 3D models and animations, you can learn more here.

        react-three-fiber is a React renderer for Three.js on the web and react-native, it is a boost to the speed at which you create 3D models and animations with Three.js, some examples of sites with 3D models and animations can be found here. react-three-fiber reduces the time spent on animations because of its reusable components, binding events and render loop, first let’s take a look at what is Three.js.

        react-three-fiber allows us to build components of threeJS code using React state, hooks and props, it also comes with the following elements:

        meshA property that helps define the shape of our models
        hooksreact-three-fiber defines hooks that help us write functions that help define user events such as onClick and onPointOver
        Component-based and render loopreact-three-fiber is component-based and renders according to a change in state or the store

        How To Use react-three-fiber

        To use react-three-fiber, you start by using the commands below:

        NPM

        npm i three react-three-fiber
        

        YARN

        yarn add three react-three-fiber 
        

        Note: For react-three-fiber to work, you’ll need to install three (Three.js) as we did above.

        Building A React 3D Ludo Dice Model And Animation Project

        Here we are going to build a 3D ludo dice model using react-three-fiber like we have in the video below.

        We will be using create-react-app to initialize our project, to do that let’s execute the command below on our terminal.

        create-react-app react-three-fiber-ludo-model
        

        The command above initializes a React project within our local machine, next let’s cd into the directory and install our packages react-three-fiber and three.

        cd react-three-fiber-ludo-model
        
        npm i three react-three-fiber
        

        Once, the packages are installed, let’s start our development server using the command

        npm start
        

        The above command should start our project development server in our browser. Next let’s open our project in our text editor of choice, inside our project src folder, delete the following files: App.css, App.test.js, serviceWorker.js and setupTests.js. Next, let’s delete all code that references the deleted files on our App.js.

        For this project, we will need a Box component for our ludo dices and our App component that’s given by React.

        Building The Box Component

        The Box component will contain the shape for our ludo dices, an image of a ludo dice and a state to always keep it in rotation. First, let’s import all the packages we need for our Box component below.

        import React, { useRef, useState, useMemo } from "react";
        import { Canvas, useFrame } from "react-three-fiber";
        import * as THREE from "three";
        import five from "./assets/five.png";
        

        In the above code, we are importing useRef, useState and useMemo. We’ll be using the useRef hook to access the mesh of the dice and the useState hook to check for the active state of the ludo dice. useMemo hook will be used to return the number on the dice. Next, we are importing Canvas and useFrame from react-three-fiber, the canvas is used to draw the graphics on the browser, while the useFrame allows components to hook into the render-loop, this makes it possible for one component to render over the content of another. Next, we imported the three package and then we imported a static image of a ludo dice.

        Next for us is to write logic for our Box component. First, we’ll start with building a functional component and add state to our component, let’s do that below.

        const Box = (props) => {
          const mesh = useRef();
        
          const [active, setActive] = useState(false);
        
          useFrame(() => {
            mesh.current.rotation.x = mesh.current.rotation.y += 0.01;
          });
        
          const texture = useMemo(() => new THREE.TextureLoader().load(five), []);
          
          return (
            <Box />
          );
        }
        

        In the code above, we are creating a Box component with props, next we create a ref called mesh using the useRef hook, we did this so that we can always return the same mesh each time.

        A mesh is a visual element in a scene, its a 3D object that make up a triangular polygon, it’s usually built using a Geometry, which is used to define the shape of the model and Material which defines the appearance of the model, you can learn about a Mesh here, You can also learn more about the useRef hook here.

        After initializing a mesh, we need to initialize a state for our application using the useState hook, here we set up the hovered and active state of the application to false.

        Next, we use the useFrame hook from react-three-fiber to rotate the mesh (ludo dice), using the code below

        mesh.current.rotation.x = mesh.current.rotation.y += 0.01;
        

        Here, we are rotating the current position of the mesh every 0.01 seconds, this is done to give the rotation a good animation.

        const texture = useMemo(() => new THREE.TextureLoader().load(five), []);
        

        In the code above, we are creating a constant called texture and passing in a react useMemo hook as a function to load a new dice roll, here the useMemo to memorize the dice image and its number. You can learn about the useMemo hook here.

        Next, we want to render the Box component on the browser and add our events, we do that below

        const Box = (props) => {
        return (
            <mesh
            {...props}
            ref={mesh}
            scale={active ? [2, 2, 2] : [1.5, 1.5, 1.5]}
            onClick={(e) => setActive(!active)}
              >
              <boxBufferGeometry args={[1, 1, 1]} />
              <meshBasicMaterial attach="material" transparent side={THREE.DoubleSide}>
                <primitive attach="map" object={texture} />
              </meshBasicMaterial>
            </mesh>
          );
        }
        

        In the code above, we are returning our Box component and wrapping it in the mesh we passed all the properties of the Box component using the spread operator, and then we referenced the mesh using the useRef hook. Next, we use the scale property from Three.js to set the size of the dice box when it’s active to 2 and 1.5 when it’s not. Last but not least, we added an onClick event to set state to active if it’s not set on default.

        <boxBufferGeometry args={[1, 1, 1]} />
        

        In order to render the dice box, we rendered the boxBufferGeometry component from Three.js, boxBufferGeometry helps us draw lines and point such as boxes, we used the args argument to pass constructors such as the size of box geometry.

        <meshBasicMaterial attach="material" transparent side={THREE.DoubleSide}>
        

        The meshBasicMaterial from Three.js, is used to draw geometries in a simple form. Here we passed the attach attribute and passing a THREE.DoubleSideprops to the side attribute. The THREE.DoubleSide defines the sides or spaces that should be rendered by react-three-fiber.

        <primitive attach="map" object={texture} />
        

        The primitive component from Three.js is used to draw 3D graphs. We attached the map property in order to maintain the original shape of the ludo dice. Next, we are going to render our Box component in the App.js file and complete our 3d ludo dice box. Your component should look similar to the image below.

        Box component for ludo 3D box

        Rendering 3D Ludo Dice Box

        In this section, we are going to render our Box component in our App.js and complete our 3d ludo box, to do that first, let’s create an App component and wrap it with a Canvas tag, this is to render our 3D models, let’s do that below.

        const App = () => {
          return (
            <Canvas>
            </Canvas>
          );
        }
        export default App;
        

        Next, let’s add a light to the boxes, react-three-fiber provides us with three components to light our models, they are as follows

        • ambientLight
          This is used to light all objects in a scene or model equally, it accepts props such as the intensity of the light, this will light the body of the ludo dice.
        • spotLight
          This light is emitted from a single direction, and it increases as the size of the object increases, this will light the points of the ludo dice.
        • pointLight
          This works similar to the light of a light bulb, light is emitted from a single point to all directions, this will be necessary for the active state of our application.

        Let’s implement the above on our application below.

        const App = () => {
          return (
            <Canvas>
              <ambientLight intensity={0.5} />
              <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} />
              <pointLight position={[-10, -10, -10]} />
            </Canvas>
          );
        }
        export default App;
        

        In the above code, we imported the ambientLight component from react-three-fiber and added an intensity of 0.5 to it, next we added a position and angle to our spotLight and pointLight component. The final step to our application is to render our box component and add a position to the ludo dice boxes, we’d do that in the code below

        <Box position={[-1.2, 0, 0]} />
        <Box position={[2.5, 0, 0]} />
        

        When done, your ludo 3D dice should look similar to the image below:

        3D ludo dice box

        A working demo is available on CodeSandbox.

        Conclusion

        react-three-fiber has made rendering 3D models and animations easier to create for React and React Native applications. By building our 3D ludo dice box, we’ve learned about the basics of Three.js alongside its components and benefits of react-three-fiber as well as how to use it.

        You can take this further by building 3D models and animations in your React and Native applications by using react-three-fiber on your own. I’d love to see what new things you come up with!

        You can read more on Three.js and react-three-fiber in the references below.

        Smashing Editorial (ks, ra, il)