Research

React Three Fiber in Production

Field Note

React Three Fiber is how we ship most of our 3D web work now. It uses the same renderer as vanilla Three.js, so it draws nothing vanilla cannot, and we use it because it turns the code from a script into components.

These are the lessons from shipping it on real sites: when R3F is the right call, where memory leaks hide, and how to make a scene look expensive on a phone that cannot afford expensive rendering.

Short answer

React Three Fiber is production-ready and is how we ship most of our 3D web work. It is a React renderer for Three.js, so it draws exactly what vanilla Three.js draws, but as reusable components with automatic disposal when they unmount. Choose react three fiber whenever React or framework islands are already in the stack, and reserve vanilla Three.js for isolated single-scene pages where bundle size rules.

Chalk illustration of a stick figure assembling nested component brackets into a glowing blue wireframe cube on a phone screen, with a clean drain at the base
The same renderer, reshaped into components.

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 says that everything that works in Three.js works here without exception, and that components render outside of React with no added overhead.

In practice the gain 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 matches how the rest of the page is already structured, and it fits 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 also favors R3F. The drei helper library covers controls, model loading, environment lighting, text and staging in a few lines each, and physics, post-processing and scroll controls all have maintained React bindings. You write meaningfully less code, and less of it handles disposal by hand.

The adoption numbers say the React path is now the mainstream one. Roughly one in three weekly installs of three arrives alongside the React renderer:

PackageWeekly npm downloadsGitHub stars
three10.1M113k
@react-three/fiber3.5M31.1k
@react-three/drei2.7M9.7k
Source: npm registry download counts and GitHub, June 2026

Why 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. With traditional page loads this never matters, because leaving the page destroys the whole context. In a single-page app it matters a great deal, because users visit your 3D page, leave and come back, and every visit that does not clean up stacks another copy of the scene in GPU memory until mobile Safari kills the tab.

Disposal is the strongest single argument for R3F. In vanilla Three.js you write that teardown yourself: traverse the scene, dispose geometries and materials, unbind events and stop the loop. We have audited sites where the missing dispose path was the entire performance complaint.

Chalk illustration of a dripping faucet labeled GPU memory slowly filling a phone-shaped bucket to overflowing
Every revisit without a dispose path adds another copy to the bucket.

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. The render loop and each asset then have exactly one owner, and no module reaches across to change 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 component tree gives you the same structure without extra work: the Canvas is the boundary, and each child component loads, animates and cleans up its own part 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 later is miserable. These are the numbers we hold to:

Why Does Baked Lighting Look Premium for Almost Nothing?

Baked lighting looks premium for almost nothing because the lighting cost moves out of the browser entirely. You light the scene in Blender, bake the result into one texture, and ship the model with a basic unlit material showing it. The scene has 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. We model the scene low-poly in Blender, light it properly there, and bake all of that lighting into a single texture. In the browser the model loads with a basic unlit material that uses the baked texture, so there are no real-time lights or shadow maps and no lighting math per frame.

R3F handles this pattern well. We load the model with useGLTF inside Suspense, apply the baked texture and add a couple of shader-driven accents such as animated particles, and the whole hero runs smoothly on a four-year-old phone. Baking 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), which costs real time for developers who only know one. On multi-page production work the maintainability win has outweighed all three.

The allocation trap is the hardest of the three to spot. Creating a new vector, material or geometry inside a render function or a useFrame callback allocates on every frame, and nothing warns you. Avoiding it takes ordinary 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?

We use R3F by default for any site with React or Astro islands in the stack, and vanilla Three.js for isolated single-scene pages where bundle size matters most. The four lessons apply 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, though R3F makes the discipline easier to keep. We take on hero scenes built to these budgets from the start rather than retrofitted to them; see how we work with clients for the shape of an engagement.

Chalk illustration of layered website panels stacked behind a glowing browser frame
The same budgets and discipline sit behind every site we ship.

Common Questions

Is React Three Fiber slower than plain Three.js?
Not meaningfully. R3F is a thin reconciler over the same renderer, and its overhead sits outside the hot render loop. Performance problems in R3F apps come from the same places as vanilla ones: draw calls, texture sizes, lights, and allocations inside the frame loop.
Can we use R3F if our site is not a React app?
Yes, through islands. Frameworks like Astro let one component hydrate as React while the rest of the page stays static HTML. The 3D scene ships as an isolated island, so you get R3F's component model without converting the whole site.
Do we need a 3D artist to get a good-looking scene?
For baked-lighting work, someone needs Blender competence for modeling, lighting, and baking. For configurators and data visualization built from primitives and purchased models, a developer with the right references can carry it alone.
Does R3F leak GPU memory when a 3D route unmounts?
Not for the objects it created. Three.js never frees GPU memory on its own, so geometries, materials, and textures persist until something calls dispose. R3F disposes what it created when a component unmounts, which removes the most common production leak.
What should we check before shipping a 3D scene to mobile?
Test frame time on a mid-range Android phone, not just the development Mac. Cap pixel ratio at 2, hold draw calls to the low hundreds with instancing, and keep real-time lights to one or two with shadows baked or off. Size every texture to what is actually visible.

Sources

SourcePublisherLink
React Three Fiber: IntroductionPoimandres (pmndrs)r3f.docs.pmnd.rs
Three.js Manual: Cleanupthree.jsthreejs.org
pmndrs/react-three-fiberGitHubgithub.com
pmndrs/dreiGitHubgithub.com
npm registry download counts: three (last week)npmapi.npmjs.org

Keep Exploring

Chalk stick figure in a hard hat presenting a little machine of blue gears it just built

Bring us the bottleneck.
We’ll build the system.

Building What's Next.