Kapeel Kokane Coder by day, content creator by night, learner at heart!

react-three-fiber: 3D rendering in the browser

8 min read 2439

3D rendering in the browser with react-three-fiber

Editor’s Note: This article was updated on 4 December 2021.

What is react-three-fiber?

As per their official documentation page:

react-three-fiber is a React renderer for three.js on the web and react-native

So, what exactly is a renderer? Renderers manage how a React tree turns into the underlying platform calls. That means we can build 3D artifacts in the form of reusable React components (staying in the React paradigm), and react-three-fiber will do the job of converting those components to the corresponding three.js primitives.

A brief intro to three.js

Now that we know that react-three-fiber is an abstraction on top of three.js, let’s spend some time getting to know what three.js is and what it does on the browser side that makes it a go-to solution when it comes to client-side 3D rendering.

Three.js is a wrapper that sits on top of WebGL (Web Graphics Library) and obfuscates all the bulk of the code that would be quite difficult to write yourself from scratch. To quote directly from the three.js documentation:

WebGL is a very low-level system that only draws points, lines, and triangles. To do anything useful with WebGL generally requires quite a bit of code and that is where three.js comes in. It handles stuff like scenes, lights, shadows, materials, textures, 3D math, etc.

With that understanding in place, let’s now get into understanding the core concepts of react-three-fiber.

Core concepts of 3D rendering with react-three-fiber


The scene is the environment that we are setting up with the help of different objects. It’s similar to the concept of a scene in a movie and it spans three dimensions as shown in the image below. It’s the place where all the action will take place.



Now that we have a three-dimensional scene and we need to display it in a browser window that is two-dimensional, we need to decide which view of the scene will be displayed. This is where the camera comes into play. We can choose from different types of cameras, with varying lenses.

react three fiber 3D render camera position


With a scene and a camera in place, we also need to manually place a light source as the scene does not light up by default. We can choose among different types of light sources like ambient (all directions) or point (coming from a particular direction), or spot (similar to a real-world spotlight).

Different directions of light -- point light, spot light, ambient light


Mesh is the object that we will be placing into our scene. It makes up the actors and things in our movie analogy. A mesh can be placed at a particular point in the coordinate system and accepts event handlers to be attached to it for interactivity. A mesh in itself is just a kind of a placeholder to which we add a geometry and a material so that it becomes visible and gains certain properties.

Mesh, geometry, and materials in 3D rendering


Geometry is how we assign different shapes to the mesh. Geometry enables us to create spheres, cubes, pyramids, etc.

cuboid geometry and pyramid geometry


Material is the component that provides material properties to the mesh. Surface-level effects (like shine, luster, colors, etc.) are assigned via material. Below is an example showing a rough, cement-like structure versus a metallic one.

react three fiber silver and purple 3D cube

Getting started with react-three-fiber

With that understanding in place, let us try to walk through a basic example on the react-three-fiber official GitHub page and try to understand the different components and their usage with respect to actual React code. Here is the link to the CodeSandbox if you would like to play around with it yourself.

Importing the bare minimum required to run React and react-three-fiber, we have:

import React, { useRef, useState } from 'react';
import { Canvas } from 'react-three-fiber';

We’ll be using the useRef and the useState Hooks. Also, the Canvas element that we have imported is the equivalent of the scene that we have come across in the last section.

Moving ahead, we create a functional component and return the scene i.e., the Canvas from it which will be attached to the DOM node that this React component is getting attached to:

export default function App() {
  return (

The next task is to place the lights. We’ll place one ambient light that lights up the entire scene. We’ll also add a spotlight to get some shadow effects and a point light as a secondary light. The camera is not mentioned explicitly and so it remains at its default position. Also, all of the three.js artifacts are available as native HTML elements in react-three-fiber, so there is no need to import them separately:

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

Observe how the spotLight and pointLight take a position attribute that controls where the light will be placed. The three attributes being passed are the x, y, and z position coordinates respectively.

Next, let’s create our main object (a pink cube) and place it into the scene. A cube is a box shape so we will use the boxBufferGeometry. But first, we can create a reusable box component with the expected behavior:

function Box(props) {
  const mesh = useRef()
  const [state, setState] = useState({isHovered: false, isActive: false})

  return (
      scale={state.isHovered ? [1.5, 1.5, 1.5] : [1, 1, 1]}
      onClick={(e) => setState({...state, isActive: !state.isActive, })}
      onPointerOver={(e) => setState({...state, isHovered: true})}
      onPointerOut={(e) => setState({...state, isHovered: false})}>
      <boxBufferGeometry args={[1, 1, 1]} />
      <meshStandardMaterial color={state.isActive ? '#820263' : '#D90368'} />

We’re making use of the useState Hook to keep a track of whether the Box is hovered/clicked. Also, we’re making it expand to 1.5 times its original size when hovered (using the scale attribute) and changing its color to purple when clicked using the color property on the mesh.

Now, we just place two instances of this box in our original scene:

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

One of the boxes is placed at position 1 on the x-axis and another at position -1 on the negative x-axis. This is how they look:

react three fiber pink 3d cube

It’s good, but we can make it a little more interesting by making the cubes rotate continuously and also, move up and down at a certain frequency. For that, we need to import useFrame from react-three-fiber. It’s an important Hook that lets us apply calculations that we want to take place on every frame computation, which is exactly what we want in this case. So we add these lines of code to the Box component that we created earlier:

useFrame(state => {
  const time = state.clock.getElapsedTime();
  mesh.current.position.y = mesh.current.position.y + Math.sin(time*2)/100;
  mesh.current.rotation.y = mesh.current.rotation.x += 0.01;

The Hook makes sure that whatever is being changed inside of it is continuously being updated at the frame rate (60 times per second). In this case, it is continuously updating the x-rotation and the y-rotation values, which provides this rotation with a changing direction effect. Also, the boxes are moving in a harmonic manner i.e., bouncing along the vertical axis. Much better!cubes bouncing and rotating purple and pink

That was a basic example to help us understand all the concepts necessary to grasp how react-three-fiber functions. Let us now get into something a little more complex and interesting.

Advanced 3D rendering in the browser with react-three-fiber

With that preliminary knowledge of react-three-fiber components and their basic usage, we can now try to create a more advanced example. We will try to build a crude version of the UI for the popular android game stack. Here is a screenshot of the rendered UI.

react three fiber 3d cubes stacked on top

This is just a UI clone of the app. The score gets incremented as and when a tile gets added to the stack (there is no tile balance check). Let’s see how this is being achieved in code. Here is a link to the CodeSandbox.

This is the main JSX code in the App.js file:

  onClick={() => generateNewBlock()}
  camera={{ position: [0, 7, 7], near: 5, far: 15 }}
  onCreated={({ gl }) => gl.setClearColor('lightpink')}>
  <ambientLight intensity={0.5} />
  <pointLight position={[150, 150, 150]} intensity={0.55} />
  <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} />
  {boxes.map((props) => (
    <Box {...props} />
  <Box position={[0, -3.5, 0]} scale={[3, 0.5, 3]} color="hotpink" />
  <Html scaleFactor={15} class="main">
    <div class="content">{`Score: ${boxes.length}`}</div>

The only major difference from the previous example is that the boxes being rendered are now coming from a boxes array. As and when the canvas is being clicked, props corresponding to the box to be newly added are pushed into the boxes array. Also, we can see an HTML section inside the Canvas that is used to render the score.

The code logic to add the new block and stop all the previous blocks is contained in this function:

function generateNewBlock() {
  const total = boxes.length
  const color = colors[getRandomInt(6)]
  let newBoxes = boxes.map((props) => ({ ...props, move: false }))
  newBoxes.push({ position: [getRandomInt(3), total * 0.5 - 3, 0], color: color, move: true })

There is also a change to the way in which useFrame is implemented so that the topmost block moves to and from:

let direction = 0.01
useFrame(() => {
  if (mesh.current.position.x > 1) {
    direction = -0.01
  } else if (mesh.current.position.x < -1) {
    direction = 0.01
  if (props.move) {
    mesh.current.rotation.y = mesh.current.rotation.y += 0.01
    mesh.current.position.x = mesh.current.position.x + direction

And that is all the code that is required to get this stacking on top kind of effect with these cuboid blocks, similar to the game.

Physics with react-three-fiber

In both of the examples that we just saw, we are simulating all the movements ourselves manually via the useFrame Hook. But, there are use cases, like in a game for instance, when we need certain attributes of the components to change with respect to time automatically (like accelerating downward under the force of gravity). This is where physics simulation comes into the picture.

By including physics in the canvas, we can enforce certain ground rules under which all the elements present inside the canvas will function. Here is a CodeSandbox illustrating physics with react-three-fiber. We’ll be using the use-cannon library in order to simulate physics inside the Canvas component. We import Physics, useSphere, and useBox from the library:

import { Physics, useSphere, useBox } from 'use-cannon'

In the current example, we are trying to simulate the falling of several balls onto the ground like so:react three fiber 3d render falling spheres simulation

The ground seen in this example is an instance of boxBufferGeometry and the balls are instances of sphereBufferGeometry that we have already seen before. But, in order to enforce the rules of physics on these, we need to attach equivalent physics elements to these meshes. This is done via the useSphere and the useBox methods.

Here’s how: 

function Ball(props) {
  const { args = [0.2, 32, 32], color } = props
  const [ref] = useSphere(() => ({ args: 0.2, mass: 1 }))

  return (
    <mesh ref={ref}>
      <sphereBufferGeometry args={args} />
      <meshStandardMaterial color={color} />

This code snippet is similar to what we did before, except for the one line:

const [ref] = useSphere(() => ({ args: 0.2, mass: 1 }))

This line is creating a sphere in the physics world with a size equivalent to 0.2 and a mass equal to 1. This API returns a ref that needs to be attached to the sphere mesh that we have. A ground is created in the same way:

function Ground(props) {
  const { args = [10, 0.8, 1] } = props
  const [ref, api] = useBox(() => ({ args }))

  useFrame(() => {
    api.position.set(0, -3.5, 0)
    api.rotation.set(0, 0, -0.08)

  return (
    <mesh ref={ref}>
      <boxBufferGeometry args={args} />
      <meshStandardMaterial color={'green'} />

Notice that we are not assigning any mass to the ground so that gravity does not act. We’re also setting a fixed position and rotation (tilt). Once we have the ground and the balls, in order for the laws of physics to act on them, they need to be placed inside the physics provider:

  gravity={[0, -26, 0]} 
  defaultContactMaterial={{ restitution: 0.6 }}
  {balls.map((props) => (
    <Ball {...props} />
  <Ground />

Notice how the Physics component takes two props that specify the gravity and restitution. Here:

  • gravity is a 3D array that determines the amount of acceleration in the x, y, and z-direction. Configured to be negative 26 here simulating a downward force in the y-direction
  • restitution is a coefficient that determines the amount of energy lost during each collision among objects in the physics world

In addition to that, we create a generator function that creates a sphere in the canvas upon each click:

function onCanvasClicked(e) {
  let newBalls = [...balls]
  const color = colors[getRandomInt(6)]
  newBalls.push({ color: color })

And that’s the entire setup that is required. With that in place, whenever a click is registered on the canvas, a new sphere is added to the physics world, which then proceeds towards the ground under the acceleration due to the configured gravity, collides with the ground/spheres, and then continues to move along the slope of the ground. That is an example of how real-world physics can be simulated in the browser using the capabilities of react-three-fiber.


The WebGL library has improved hugely as far as performance is concerned in the past few years. Three.js capitalizes on those gains and brings the same speed to its APIs. And react-three-fiber performance is bottlenecked by three.js and the GPU. It means that react-three-fiber by itself does not introduce any bottlenecks as far as the rendering is concerned.

Quoting from the official page:

Rendering performance is up to three.js and the GPU. Components participate in the render loop outside of React, without any additional overhead. React is otherwise very efficient in building and managing component-trees, it could potentially outperform manual/imperative apps at scale.


If you are already building simple web apps with React and are looking to go the 3D way, then react-three-fiber becomes a go-to solution.

The benefits include:

  • The performance of the improved WebGL library
  • The range of three.js APIs and artifacts directly available
  • No need to leave the React ecosystem

With those benefits in mind, react-three-fiber turns out to be a worthy candidate to at least give a try and explore when it comes to rendering three dimensions inside the two that are made available to us by the browser. If that does not convince you yet, check out more of these awesome examples on CodeSandbox. Cheers!

LogRocket: Full visibility into your production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket combines session replay, product analytics, and error tracking – empowering software teams to create the ideal web and mobile product experience. What does that mean for you?

Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay problems as if they happened in your own browser to quickly understand what went wrong.

No more noisy alerting. Smart error tracking lets you triage and categorize issues, then learns from this. Get notified of impactful user issues, not false positives. Less alerts, way more useful signal.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

Kapeel Kokane Coder by day, content creator by night, learner at heart!

One Reply to “react-three-fiber: 3D rendering in the browser”

Leave a Reply