Add 3D to React: Intro to React-Three-Fiber
Simplify 3D in React using Three.js and React-Three-Fiber
Have you ever wondered how some sites look better than others? How some sites have a 3D effect that makes them look more appealing? Well, that's because they use 3D libraries like Three.js.
When it comes to creating 3D graphics in the browser, Three.js is the go-to JavaScript library. It provides the tools and abstractions needed to create and render complex 3D scenes, but using it directly in React can be tricky. Fortunately, React-Three-Fiber package bridges the gap, allowing React developers to create and manage 3D scenes declaratively making it like creating standard UI components. Alongside it, there are multiple helper libraries like drei that offer a collection of useful components to enhance the development process and ease the creation of 3D scenes.
In this blog post, we’ll explore these tools, how they work together, and provide examples to demonstrate the difference between using plain Three.js and React-Three-Fiber.
What is Three.js?
Three.js is a powerful JavaScript library that abstracts WebGL to create 3D graphics in the browser. It provides essential tools for building 3D environments, stunning visual effects, games, and more, including:
- Scenes: Containers for 3D objects, lights, and cameras.
- Cameras: Define the view from which the scene is rendered.
- Meshes: Objects that are made up of geometry (shape) and material (appearance).
- Lights: Illuminate the 3D objects.
With Three.js, you get full control over your 3D scene, but it can involve a lot of boilerplate code, especially when managing things like rendering loops or state updates. Here’s an example of creating a basic cube in Three.js.
Example: Basic Cube with rotation in Three.js
import * as THREE from "three";
// Create a scene
const scene = new THREE.Scene();
// Create a camera (perspective)
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.z = 5;
// Create a renderer
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Create a cube geometry
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// Animation loop
function animate() {
requestAnimationFrame(animate);
// Rotate the cube
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
// Render the scene
renderer.render(scene, camera);
}
animate();
What is React-Three-Fiber?
React-Three-Fiber (R3F) is a React renderer for Three.js that allows you to build 3D scenes declaratively in JSX, the way you build your React components. It brings the power of React’s component-based architecture to 3D graphics and makes managing the lifecycle of objects easier. You no longer need to manually manage rendering loops, state, or updates to the 3D scene, as R3F handles this internally.
It wraps the Three.js API and makes it compatible with React’s reconciliation system (R3F transforms the imperative nature of Three.js into a declarative one, aligning it with React’s component lifecycle and rendering logic), allowing for a more seamless and declarative experience. You can now define your 3D scene as a React component, making it easier to integrate into an existing React project.
Example: Basic Cube with rotation in React-Three-Fiber
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
import { BoxGeometry } from "three";
function App() {
const cube = useRef<BoxGeometry | null>(null);
// animation loop - custom hook provided by R3F to run code on every frame
useFrame(() => {
if (cube.current) {
cube.current.rotateX(0.01);
cube.current.rotateY(0.01);
}
});
return (
<Canvas>
<mesh rotation={[0.5, 0.5, 0]}>
<boxGeometry args={[1, 1, 1]} ref={cube} />
<meshBasicMaterial color="green" />
</mesh>
<OrbitControls />
</Canvas>
);
}
export default App;
In this example, OrbitControls
allows the user to move the camera around the cube using mouse interactions. With drei
, you can save time by using these pre-built components, which reduce the amount of boilerplate code. It's important to mention that useFrame
is a custom hook provided by R3F that allows you to run code on every frame of the animation loop.
Taking a look at this example here’s what changes with React-Three-Fiber:
- Declarative JSX: You can define Three.js components like
<mesh>
,<boxGeometry>
, and<meshBasicMaterial>
directly in JSX, similar to other React components. - No manual rendering loop: R3F automatically handles updates and rendering. No need for requestAnimationFrame calls.
- Reactivity: You can easily hook into React’s state and props to control the behavior of your 3D objects.
Key Differences Between Three.js and React-Three-Fiber
Imperative vs. Declarative:
- Three.js: Requires imperative coding, where you have to manually update the scene and objects.
- React-Three-Fiber: Embraces the declarative nature of React, enabling you to write 3D scenes as components.
Rendering Management:
- Three.js: You control the render loop manually.
- React-Three-Fiber: The render loop and updates are abstracted and automatically handled by R3F. Disclaimer: you can always opt-out of the automatic rendering loop and manage it manually if needed by using the
useFrame
hook.
Integration with React:
- Three.js: You need to figure out how to integrate 3D rendering with React’s state management and component lifecycle.
- React-Three-Fiber: Naturally fits into React’s ecosystem, allowing you to control 3D objects with React’s state, props, and hooks.
Ecosystem:
- Three.js: Large and versatile, but you’ll need to find or build certain common components.
- React-Three-Fiber: Grows the Three.js ecosystem by adding packages like pmndrs/drei that give you access to ready-made components like OrbitControls, Text, and GLTFLoader.
Conclusion
If you're a React developer looking to create 3D experiences, React-Three-Fiber offers a powerful way to integrate Three.js into your workflow. It brings the benefits of React’s declarative nature, making it easier to build and manage 3D scenes.
With helper libraries like:
- drei/react-postprocessing Effects and Postprocessing
- jotai/zustand State Management
- react-three-rapier/cannon Physics
the development process becomes even smoother, allowing you to focus on creating compelling 3D experiences rather than managing boilerplate code.
For smaller, simpler projects or where full control over the rendering process is necessary, Three.js is still a great choice. However, if you’re already invested in the React ecosystem, React-Three-Fiber should be your go-to solution for working with 3D in the browser.