What Is React Three Fiber?
React Three Fiber is a React renderer for Three.js. You declare a scene as JSX components instead of writing imperative setup code, and props map directly to Three.js properties. It produces identical output to vanilla Three.js because it uses the same renderer underneath, with no meaningful overhead added.
Instead of imperatively creating a scene, a camera, a renderer, and a loop, you declare the scene as JSX: a Canvas component, meshes as children, lights as siblings. A useFrame hook replaces the hand-rolled animation loop. The official documentation puts it flatly: everything that works in Three.js works here without exception, and components render outside of React with no added overhead.
The practical win is composition. A product viewer, a particle field, or a baked hero scene becomes a component you can drop into any React tree, pass props to, and reuse across pages. For the sites we build on Astro islands or Next.js, that maps exactly to how the rest of the page is already structured, and it pairs naturally with the scroll-driven patterns we cover in how scroll animation websites work.
When Should You Choose R3F Over Vanilla Three.js?
Choose R3F when the site is React-based, the 3D work must be reused across pages, or more than one person will maintain it. Choose vanilla Three.js when there is no React in the stack, when bundle size for a single isolated scene matters most, or when you are doing renderer-level work where the abstraction gets in the way.
The ecosystem tips the scale further toward R3F. The drei helper library covers controls, model loading, environment lighting, text, and staging in a few lines each. Physics, post-processing, and scroll controls all have maintained React bindings. You write meaningfully less code, and less code that touches disposal by hand.
The adoption numbers say the React path is now the mainstream one, not the exotic one. Roughly one in three weekly installs of three arrives alongside the React renderer:
| Package | Weekly npm downloads | GitHub stars |
|---|---|---|
| three | 10.1M | 113k |
| @react-three/fiber | 3.5M | 31.1k |
| @react-three/drei | 2.7M | 9.7k |
Weekly npm downloads, June 2026
Source: npm registry download counts APIWhy Does Disposal Make or Break Memory in Single-Page Apps?
Three.js never frees GPU memory automatically. Geometries, materials, and textures persist until you call dispose, so every revisit to a 3D route in a single-page app can stack another copy of the scene in GPU memory. R3F disposes the objects it created when a component unmounts, which removes the most common production leak.
The three.js manual is explicit that this is by design: the library cannot clean these resources up automatically, and a single 1024-pixel texture can hold four to six megabytes of GPU memory. On a traditional page load that never matters: navigate away and the whole context dies. On a single-page app it matters enormously, because users navigate to your 3D page, leave, and come back, and every visit that does not clean up stacks another copy of your scene in GPU memory until mobile Safari kills the tab.
This is the strongest single argument for R3F. In vanilla Three.js you must write that teardown yourself, traversing the scene, disposing geometries and materials, unbinding events, and stopping the loop. We have audited sites where the missing dispose path was the entire performance complaint.

How Should You Structure a Production Scene?
Give the scene one entry point that owns the renderer, camera, sizing, and time, then split the world into modules or components that each own their own assets and cleanup. One owner for the render loop, one owner per asset, and no module reaching across to mutate another module's mesh.
Hero-scene code rots fast when everything lives in one file. In vanilla projects we use a single Experience entry point with the world split into modules behind it. In R3F the same structure falls out of the component tree naturally: the Canvas is the boundary, and each child component loads, animates, and cleans up its own slice of the world.
What Belongs in a Mobile Performance Budget?
Set the budget before you build: pixel ratio capped at 2, draw calls held to the low hundreds with instancing, one or two real-time lights with shadows baked or off, textures sized to what is actually visible, and a frame-time check on a mid-range Android phone before launch, not just the development Mac.
Every scene we ship gets a performance budget up front, because retrofitting one is miserable. The numbers we hold to:
- Pixel ratio capped at 2, no matter what the device reports. Rendering at 3x on a phone burns battery for sharpness nobody perceives.
- Draw calls in the low hundreds, using instancing when many objects share a geometry and material.
- Real-time lights kept to one or two, with shadows baked or off on mobile.
- Textures sized to what is actually visible, not to what the designer exported.
- A frame-time check on a mid-range Android phone before launch, not just on the development Mac.
Why Does Baked Lighting Look Premium for Almost Nothing?
Baking moves the lighting cost out of the browser entirely. You light the scene properly in Blender, bake the result into a single texture, and ship the model with a basic unlit material wearing it. The scene shows soft global illumination that real-time rendering cannot afford, at the render cost of a textured cube.
The best-looking scenes we ship are usually the cheapest to render. The workflow: model the scene low-poly in Blender, light it properly there, then bake all of that lighting into a single texture. In the browser, the model loads with a basic unlit material wearing the baked texture. No real-time lights, no shadow maps, no lighting math per frame.
R3F handles this pattern well: load the model with useGLTF inside Suspense, apply the baked texture, add a couple of shader-driven accents like animated particles, and the whole hero runs smoothly on a four-year-old phone. It is the core technique behind most of our 3D website design work, and we wrote more about where it fits in our note on Three.js for business sites.
What Are the Honest Tradeoffs of R3F?
R3F adds React as a dependency, which is wrong for a one-canvas brochure page. The abstraction can hide per-frame allocations created inside render functions, and debugging spans two layers, React and Three.js, a real tax on developers who only know one. On multi-page production work the maintainability win has outweighed all three.
The allocation trap is the sneakiest of the three. Creating a new vector, material, or geometry inside a render function or a useFrame callback allocates every frame, and nothing warns you. The fix is old React discipline: memoize what persists, mutate what changes, and profile on real hardware instead of trusting the development machine.
Where Did We Land on R3F Versus Vanilla?
R3F by default for any site with React or Astro islands in the stack, vanilla Three.js for isolated single-scene pages where bundle size rules. The four lessons travel either way: dispose deliberately, structure the scene like an app, set the mobile budget before modeling starts, and bake lighting whenever the scene allows it.
The framework choice matters less than the discipline, but R3F makes the discipline easier to keep. If you want a hero scene built to these budgets rather than retrofitted to them, that is exactly the kind of project we take on. See how we work with clients for the shape of an engagement.



