Getting started with React Three Fiber (r3f) with working Hot Module Replacement.

Together lets make a very lightweight React Three Fiber (r3f) boilerplate that we can use as a jumping off point for future projects

In this tutorial we are going to build a barebones starter project with React Three Fiber and Vite. We are going to keep the setup as lightweight as possible so you can move fast and explore.

What is React Three Fiber

React Three Fiber is a wrapper for the web graphics library Three.js. Β It allows you to use Three.js objects as if they were React components with the component nesting matching a scene hierachy. Its awesome! You can scan it's homepage here but it doesn't have a 'from zero' tutorial so thats what this is.

Read about r3f here!

GitHub - pmndrs/react-three-fiber: πŸ‡¨πŸ‡­ A React renderer for Three.js
πŸ‡¨πŸ‡­ A React renderer for Three.js. Contribute to pmndrs/react-three-fiber development by creating an account on GitHub.

What is Vite

Vite is the current darling child of the bundler world boasting being easier and faster than webpack with lots of bells and whistles built in. The specifics don't matter for this tutorial so you can read about it here if you want too.

Vite
Next Generation Frontend Tooling

Pre-requisites

  • Node.js and npm installed
  • Understand basic JavaScript
  • Some understanding of React

First Steps


In the terminal navigate to where you want the project and run

 npm create vite@latest

If you do not have Vite installed you'll be prompted to select a version to install from the dropdown. Select the latest although this tutorial was initially written with 4.48.0. Next you'll be prompted to name the project then you can answer the following questions like so:

  • Project template - Choose 'Vanilla' As we are going to get going from scratch we can select Vanilla from the setup options.
  • Typescript or Javascript - We are going to use plain ol' JS here to keep things simple so choose 'Javascript'.

You'll now be prompted to run some commands to get going with your Vite project.

  # navigate to the project folder that was created
  cd your-project-name
  
  # install dependencies 
  npm install
  
  # start the development server
  npm run dev

You can run these as instructed.

Strip out the Vite boilerplate

Vite will have created a counter.js and the main.js file in the root of the project - you can delete these. You can also delete the javascript.svg file and styles.css file.

Vite is likely to complain a lot during these steps as we are deleting files referenced in other files, but we are going to remove the links so its no big deal.

Next create we need to create the structure for the root of our react project. Create a folder in the root called 'src' and within that place an empty index.jsx file. This will be the entry point of our app so lets link it. In the root of the folder there is a html file, change the link within the script tag from the (now) non existent main.js to the new src/index.jsx. It should look like this now:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite App</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/index.jsx"></script>
  </body>
</html>

Install React

Time to install react and set up some of the basics. Stop vite with ctrl+c within the terminal (any time you want to restart the dev server with npm run dev) and run the following command

npm i react react-dom

Once this is installed, in the empty index.jsx file we made in the previous step you can add the following:

import * as React from "react";
import * as ReactDOM from "react-dom/client";
import App from "./App";

ReactDOM.createRoot(document.getElementById("app")).render(<App />);

Here we get the app div (defined within the root html file) and append React App component at the root. All our code will be rendered in this base component. You might notice that Vite is grumbling because we don't have an App file... So let's create that now. In the same directory as your index.jsx (within ./src) create the first jsx file were your app can live - App.jsx

We will make this very simple to start, remember we are just creating scaffolding. Within the app.jsx file you can create

import * as React from "react";

export default function App() {
  return (
    <>
      <h1>Hello World!</h1>
    </>
  );
}

Vite doesn't yet know the best way to handle the React code here so Vite is going to require some configuration.

Create a filed named vite.config.js at the root of your project. We need to import the function for the defition of our configuration from Vite so you can do that like this:

import { defineConfig } from "vite";

export default defineConfig({});

So that Vite can deal with our react code, as well as some other out of the box features such hot refresh we can install the plugin @vitejs/plugin-react from npm:

npm i @vitejs/plugin-react

then define its use in our new vite.config.js like so

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
});

Your basic react app will now show at the url within your test server (it might require Β a restart) with hot module replacement working! Try changing the hello world text and see the change be reflected in the browser upon saving.

Installing React Three Fiber

Lets move onto React Three Fiber. Back to your terminal, lets run the following to install three.js and react three fiber for use.

npm install three @types/three @react-three/fiber

Once these are installed we can make our first basic scene. We are going to use the built in methods to just create a simple box. To do this we create a canvas element, and within this canvas element we can define and ambient light, a spotlight and a mesh with box geometry and standardmaterial.

You can see how this is done below. As we no longer need our 'Hello World' text lets replace the App.jsx file completely with our react-three-fiber code.

import * as React from "react";
// import the canvas element from r3f
import { Canvas } from "@react-three/fiber";

export default function App() {
  return (
    // create the canvas
    <Canvas>
      // create an ambient light
      <ambientLight />
      
      // create a pointlight and give it a position
      <pointLight position={[10, 10, 10]} />
    
      // create a mess and rotate it slightly
      <mesh rotation={[10, 15, 6]}>
          
        // create a box geometry of a size of 2 on all axis 
        <boxGeometry args={[2, 2, 2]} />
            
        // give the mesh the standard pbr material with preset color of hotpink
        <meshStandardMaterial color="hotpink" />
      </mesh>
    </Canvas>
  );
}

Hopefully you notice during this time that the hot module replacement is working perfectly and you can alter the position and size of the box and see the changes reflected live on save. If everything has gone well your browser window should look like this

You will notice that the canvas isn't taking the full height like you might want it too. By default the canvas container takes the height of its parent so lets wrap the canvas in a full height element.

Wrap the element like so:

export default function App() {
  return (
    <div
      style={{
        width: "100vw",
        height: "100vh",
        position: "absolute",
        top: 0,
        left: 0,
      }}
    >
      <Canvas>
         ...canvas stuff
      </Canvas>
    </div>
  );
}

We will now have a full screen canvas absolutely positioned to stop any scroll bars. Yours should look like this:

Lets improve the scene

First off the white background is hurting my eyes, let's make it a darker color. We can do that the simple way of setting the background color of the surrounded div to black. In the styles property of the container div simply add
'backgroundColor : black'.
You could also achieve the same effect by creating a color component and attaching it to the background. Thats achieved by adding:
<color attach="background" args={["black"]} />
within the canvas component. I'll be keeping it simple by changing the html background color.

One of the amazing things about r3f is the ecosystem of packages around it. You can get so many awesome tools by taking advantage of the ecosystem. Here are some of my favourite packages made by the awesome team:

Lets use create an orbiting camera using the <OrbitControls /> from within drei.
First import it with the the following command in the terminal. You might need to restart the Vite dev server after.

npm i @react-three/drei

Then you can just add it within the canvas. It'll use the default three camera and work. Now you have a basic cube you can spin! Here is your final scene

import * as React from "react";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

export default function App() {
  return (
    <div
      style={{
        width: "100vw",
        height: "100vh",
        position: "absolute",
        top: 0,
        left: 0,
        backgroundColor: "black",
      }}
    >
      <Canvas>
        <ambientLight />
        <pointLight position={[10, 10, 10]} />
        <mesh rotation={[10, 15, 6]}>
          <boxGeometry args={[2, 2, 2]} />
          <meshStandardMaterial color="hotpink" />
        </mesh>

        <OrbitControls />
      </Canvas>
    </div>
  );
}

Now experiment!

I've got you this far! Go and explore the ecosystem and amazing examples and make something wonderful for yourself! You made this from scratch so you have full control over the app with zero bloat. Congratulations!

You can find the code here:

GitHub - simonharrisco/r3f-vite-boilerplate: My starting point for a new React Three Fiber project using Vite. Uses plugin-react for hmr and stuff. Absolute barebones.
My starting point for a new React Three Fiber project using Vite. Uses plugin-react for hmr and stuff. Absolute barebones. - GitHub - simonharrisco/r3f-vite-boilerplate: My starting point for a new...

Any questions DM me on Twitter: @SimonHarrisCo