React Three Fiber: Three.js as a React Renderer

React Three Fiber (R3F) is a React renderer for Three.js: it lets you build a Three.js scene declaratively as React components (<mesh>, <boxGeometry>, <meshStandardMaterial>) instead of imperative new THREE.X() calls, while still compiling down to the exact same Three.js objects underneath. Nothing is reimplemented, so every Three.js feature, loader and technique is fully available.

Last updated . Verified against three.js r181.

Read the FAQJump to code

What React Three Fiber actually is

R3F is a renderer, not a wrapper or an abstraction layer that hides Three.js. It maps JSX elements directly onto Three.js constructors: <mesh> creates a THREE.Mesh, <boxGeometry args={[1,1,1]}> creates a THREE.BoxGeometry, props become constructor arguments and property assignments. Under the hood, your component tree is the Three.js scene graph. Inspecting it in devtools shows real Three.js objects, and any Three.js API you already know (geometries, materials, loaders, raycasting) works exactly the same, just expressed as JSX instead of imperative calls.

This matters practically: nothing in the Three.js ecosystem is off-limits. A GLTFLoader-loaded model, a custom TSL shader, a post-processing pass all drop in the same way they would in vanilla Three.js, just declared as components.

When to use R3F over vanilla Three.js

Use R3F when:

  • You're already building in React and want your 3D scene's state to compose naturally with the rest of your app's React state, props and context, with no manual bridge code between "React world" and "Three.js world."
  • You want React's component model (reusable, composable pieces) for scene organization. A <Car> component that itself renders wheels, a body and lights is just... a React component.
  • You want access to the drei/postprocessing/physics ecosystem described below, much of which assumes R3F.

Stick with vanilla Three.js when:

  • Your project isn't a React app, or 3D is a small isolated feature bolted onto a non-React page. Pulling in React and R3F for one canvas is unnecessary weight.
  • You need very fine-grained control over the render loop's exact imperative sequencing in a way that fights React's declarative model.

R3F's runtime overhead versus vanilla Three.js is minimal. It's the same Three.js underneath, so this decision is almost entirely about developer ergonomics and codebase fit, not performance.

The ecosystem: drei, postprocessing, physics

R3F's real advantage over vanilla Three.js in practice is the ecosystem that has grown around it:

  • drei: a library of ready-made helper components: <OrbitControls>, <Environment> (HDRI lighting in one line), <Text>, <Html> (mix DOM content into the 3D scene), <PerspectiveCamera>, and dozens more. Genuinely removes a huge amount of boilerplate for common needs.
  • @react-three/postprocessing: a declarative wrapper around Three.js's post-processing passes (bloom, depth-of-field, chromatic aberration) as JSX components.
  • @react-three/rapier / @react-three/cannon: see the physics guide. Hook-based physics with automatic mesh-to-body syncing, removing the manual sync loop vanilla Three.js physics requires.
  • @react-three/fiber's own hooks: useFrame for per-frame logic, useThree to access the scene/camera/renderer, useLoader for suspense-integrated asset loading.

This ecosystem is the single biggest practical reason teams choose R3F over vanilla Three.js for anything beyond a simple scene. See the React Three Fiber vs Three.js comparison for how that tradeoff plays out end to end.

Code

Vanilla Three.js (r181)
1import * as THREE from 'three'
2
3const scene = new THREE.Scene()
4const camera = new THREE.PerspectiveCamera(50, innerWidth / innerHeight, 0.1, 100)
5camera.position.z = 5
6const renderer = new THREE.WebGLRenderer()
7renderer.setSize(innerWidth, innerHeight)
8document.body.appendChild(renderer.domElement)
9
10const mesh = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshStandardMaterial({ color: 'orange' }))
11scene.add(mesh)
12scene.add(new THREE.DirectionalLight(0xffffff, 2).translateZ(3))
13
14function animate() {
15 mesh.rotation.y += 0.01
16 renderer.render(scene, camera)
17}
18renderer.setAnimationLoop(animate)
React Three Fiber: the same scene
1import { Canvas, useFrame } from '@react-three/fiber'
2import { useRef } from 'react'
3
4function Box() {
5 const mesh = useRef()
6 useFrame(() => { mesh.current.rotation.y += 0.01 })
7
8 return (
9 <mesh ref={mesh}>
10 <boxGeometry />
11 <meshStandardMaterial color="orange" />
12 </mesh>
13 )
14}
15
16export default function App() {
17 return (
18 <Canvas camera={{ position: [0, 0, 5] }}>
19 <directionalLight intensity={2} position={[0, 0, 3]} />
20 <Box />
21 </Canvas>
22 )
23}

Learn this properly

Learn Practical TSL

Your First Node Material

Node materials work identically whether you're driving Three.js directly or through React Three Fiber.

Start the lesson (6 minutes)

Frequently asked questions

Is React Three Fiber slower than vanilla Three.js?

No. R3F compiles JSX directly to the same Three.js objects vanilla code would create, with negligible reconciliation overhead. Performance differences in real projects come from application code and scene complexity, not from R3F itself.

Do I need to know Three.js to use React Three Fiber?

Yes, at least the fundamentals: geometries, materials, lights, cameras. R3F changes how you express a scene (JSX instead of imperative calls), not what a scene is made of, so Three.js concepts transfer directly.

What is drei?

A companion library of ready-made React Three Fiber components for common needs, such as controls, environment lighting, text, and HTML overlays, that removes a large amount of boilerplate you'd otherwise write by hand.

Can I use any Three.js library or loader with React Three Fiber?

Yes. Since R3F produces real Three.js objects under the hood, any loader, shader, or technique that works in vanilla Three.js works in R3F, usually wrapped in a small custom hook or component if no R3F-specific wrapper already exists.

Keep reading