react-three-fiber-drei
React Three Fiber + drei
When to use this
- You are building any 3D scene, product viewer, or WebGL background inside a React application and want declarative, component-based scene composition instead of imperative Three.js calls.
- You need common 3D UI patterns (orbit-controllable product view, floating/animated objects, glass/transmission materials, scroll-linked 3D, HTML overlays anchored to 3D positions) and want drei's pre-built helpers instead of hand-rolling them.
- You need Suspense-based asset loading (GLTF models, textures) that integrates with React's existing loading/error boundary patterns.
- Do NOT use this outside a React codebase; use
three-js-fundamentalsdirectly for vanilla JS/other frameworks. Do NOT reach for R3F for a simple full-screen shader background with no real scene graph; a raw<canvas>+ WebGL context perglsl-fragment-backgroundsis lighter weight for that narrow case, though R3F'sshaderMaterialvia drei is also a reasonable choice if you're already in an R3F codebase.
Mental model
R3F is a React reconciler for Three.js: every Three.js class becomes a lowercase JSX tag (<mesh>, <boxGeometry>, <meshStandardMaterial>), and R3F translates your component tree into real Three.js object instantiation/scene.add() calls behind the scenes, keeping them in sync as your React state changes, exactly like ReactDOM does for actual DOM nodes. Props on these tags map to constructor arguments (via an args array prop) or direct property assignment (any other prop is set directly on the underlying instance, so <meshStandardMaterial color="hotpink" roughness={0.4} /> sets .color and .roughness on a real THREE.MeshStandardMaterial).
The <Canvas> component sets up the renderer, an automatic resize observer, and a default camera/scene, and crucially it establishes its own render loop internally: R3F calls renderer.render() for you every frame by default (frameloop="always"), which is different from vanilla Three.js where you write that loop yourself. useFrame(callback) is how you hook per-frame logic (animation, physics step, uniform updates) into that same loop; it must be called inside a component that is a descendant of <Canvas>, and it receives (state, delta) where state gives you the current THREE.Clock, camera, scene, gl renderer, pointer position, and more.
The rules-of-hooks gotcha specific to R3F: useFrame, useThree, useLoader all only work inside the R3F render tree (inside <Canvas>), not in arbitrary parent components, because they read from an R3F-specific React context that only exists there. A common mistake is trying to call useThree() in a component that renders the <Canvas> itself rather than one that is a child of it.
drei is a grab-bag of pre-built abstractions over common Three.js patterns, all written as regular R3F components/hooks, so they compose normally with everything else. useGLTF wraps GLTFLoader in a Suspense-compatible hook with automatic caching (repeated calls with the same URL return the cached parse). Environment sets up image-based lighting from an HDRI in one line instead of manually configuring a PMREMGenerator. ScrollControls bridges native page/canvas scroll into R3F-readable progress values, which is the standard way to build scroll-driven 3D scenes in this ecosystem.