If a team is already a React shop and they're adding their first 3D feature, the question of whether to use React Three Fiber comes up almost automatically. The team's instinct is to default to R3F because it matches the rest of their stack. The instinct is usually right. It's not always right.
I've shipped projects on both. The decision has more nuance than the React-default reflex suggests. This article is the framework I use when scoping that choice for a client — including when I'd push back on R3F even for a React-heavy team.
The honest TL;DR
Use R3F when the 3D feature is one component among many in a React app. Use vanilla Three.js when the 3D feature is the application — or when the 3D code needs to outlive a frontend framework migration.
That summary will already feel familiar to engineers who've shipped both. The rest of this article is the reasoning behind that summary, and the cases where it gets more complicated.
What R3F is, briefly
React Three Fiber wraps Three.js in a React-component model. Instead of imperatively creating and adding meshes to a scene, you describe the scene as JSX and let R3F reconcile changes the way React reconciles DOM changes. Lights, meshes, materials, cameras — all become React components.
For a senior React developer, this feels deeply familiar. The mental model is the same as building any React UI. Component composition, props-based configuration, hooks for state and side effects, declarative rendering. That familiarity is the central value proposition.
The library is mature, actively maintained, and has a strong ecosystem (@react-three/drei for common patterns, @react-three/cannon and @react-three/rapier for physics, @react-three/postprocessing for visual effects). For a React team looking to add 3D, the ecosystem is comprehensive enough to ship serious work.
When R3F is the right call
The cases where I'd recommend R3F without hesitation:
The 3D feature is one component in a larger React app. A configurator embedded in a Next.js commerce site. A 3D explainer on a marketing page built in Gatsby. A product viewer inside a SaaS dashboard. The 3D component talks to the rest of the React app via props and context, just like every other component does. R3F makes this trivially easy and the alternative — bridging vanilla Three.js into a React app — is more work than it sounds.
The team is React-fluent and not 3D-fluent. A team that knows hooks, suspense, and component patterns inside out, but has never set up a Three.js scene before, will be productive in R3F faster than in vanilla. The familiar abstractions give them traction immediately.
The project depends on React state for scene logic. When the 3D scene's behavior is driven by application state — Redux, Zustand, React context — R3F's natural integration with React's state model removes a category of bugs that bridging would create.
The codebase has long-term React investment. A codebase that will live in React for the foreseeable future benefits from R3F's homogeneity. New developers join, recognize the patterns, and contribute immediately.
When vanilla Three.js is the right call
The cases where I'd push back on the R3F default:
The 3D experience is the entire product. When the page is a 3D experience — full-screen, immersive, the React app is essentially a thin shell around the canvas — R3F adds reconciliation overhead and abstractions that don't pay back. Vanilla Three.js gives more direct control over the render loop and easier optimization paths. The classic example: a portfolio site that's 90% 3D, with a thin overlay of HTML for navigation. R3F can do this; it's just not the simplest way.
Performance is a primary constraint at the limit. R3F's reconciliation has a real cost. For most projects it's negligible. For projects with thousands of dynamic objects, custom shaders, complex post-processing chains, or sub-millisecond render budgets — vanilla Three.js gives more headroom. A senior threejs developer working at the performance ceiling will reach for vanilla every time.
The 3D code needs to outlive the framework. If there's any chance the host application will migrate frameworks (React → SolidJS, Vue → Svelte, plain HTML → some new build system), vanilla Three.js code is portable. R3F code is not. For a project with a 5+ year horizon, this matters more than it sounds.
The team has strong vanilla Three.js experience. A senior 3D developer who's spent years in vanilla Three.js will be more productive in vanilla — even on a React project — than in R3F. The abstraction is helpful for newcomers and can be friction for experts.
The project doesn't have a React stack at all. If the host site is WordPress, Webflow, or plain HTML, R3F is wrong by definition — it requires React. Vanilla Three.js is the only honest choice here.
The cases that look like R3F but aren't
A few patterns that come up often where the R3F-or-vanilla decision is more nuanced than it first appears.
A "mostly static" 3D scene inside a React app. The team has a React app and wants to add a 3D scene that, in practice, doesn't change much — a hero element, a background animation, a single rotating product. The React-default reflex says R3F. The senior call is often "vanilla Three.js, mounted into a React component, with the rest of the React app untouched." The 3D code stays simple, the React app stays simple, the bridge between them is one useEffect and one ref. R3F's reconciliation isn't earning anything here.
A configurator with heavy custom shader work. R3F handles custom shaders fine, but the workflow is friction-heavy compared to vanilla. The shader code itself is the same. The setup, debugging, hot-reload, and iteration speed are smoother in vanilla. For projects where the shaders are the product, I'd lean vanilla.
A scene with a lot of imperative animation. GSAP timelines, complex camera scripts, intricate sequenced animations — these are imperative by nature. Forcing them into a declarative R3F structure can feel like fighting the framework. Vanilla Three.js with GSAP is often cleaner.
The hidden cost of R3F: hiring
R3F-specific knowledge is rarer than vanilla Three.js knowledge. A team that ships their project on R3F and then tries to hire a maintainer two years later will find a smaller pool of qualified candidates than if they'd shipped on vanilla.
This is real but easy to over-weight. Most React-fluent senior developers can pick up R3F in a week. Most vanilla Three.js developers can pick up R3F in a few days. The hiring constraint isn't that R3F is exotic — it's that the best 3D web developers are often more vanilla-comfortable, and R3F-only roles can feel limiting to them.
For a team with long-term React commitment and a pipeline of 3D work, this isn't a real issue. For a team doing one 3D project and then moving on, it's worth thinking about whether the maintenance team will be able to find qualified developers in 2028.
What I do on real projects
When I'm hired for an interactive 3D web development engagement, the framework conversation usually goes like this:
- Is the host site React? If no, vanilla. End of conversation.
- Is the host site React, and is the 3D feature embedded in a larger app? If yes, R3F is probably right.
- Is the 3D feature the dominant content of the page? If yes, vanilla regardless of host stack.
- Does the team have anyone with strong R3F or vanilla preference? If yes, follow the team's strength.
- Is performance the binding constraint? If yes, vanilla.
- Is the project longevity longer than the host framework's likely lifespan? If yes, vanilla.
After working through that, the framework choice usually becomes obvious. The cases where it's genuinely ambiguous are rare, and in those cases the right answer is usually "either works; pick the one your team will be happiest in."
The summary I'd give a CTO
R3F is excellent for the case it's designed for: a React app with 3D as one feature among many. Vanilla Three.js is the right call for everything else — full-page 3D experiences, performance-bound projects, non-React stacks, and any project where the 3D code's longevity matters.
The mistake to avoid is letting the React-default reflex make the decision for the whole project. The framework choice should follow from the project, not the team's existing comfort. A senior threejs developer for hire will tell you which one is right for your specific project in the first conversation. The wrong choice isn't fatal — both can ship the same end result — but the wrong choice adds friction that compounds over the engagement.
If you're scoping a 3D project right now and the framework decision feels arbitrary, that's the conversation worth having early. Twenty minutes is usually enough to resolve it.