React Three Fiber v9 + drei v10
Escenas 3D declarativas en React. Complementa la skill threejs (vanilla Three.js imperativo).
Modelo Mental
React y Three.js manejan dominios distintos que se comunican via refs:
- React es dueño del scene graph: monta, desmonta, reconcilia componentes JSX → objetos Three.js
- Three.js es dueño del render loop:
useFramecorre cada frame, muta refs directamente - Regla de oro: React para estado y estructura, refs para mutaciones per-frame
React tree (JSX, state, props)
↕ refs / props
useFrame callbacks (per-frame mutations)
↕ escribe en Three.js objects
WebGLRenderer (render en priority 0)
Cuando Usar Esta Skill vs threejs
| Escenario | Skill | |-----------|-------| | Proyecto React + JSX para 3D | r3f (esta) | | Vanilla Three.js imperativo | threejs | | Conceptos de materiales, luces, shadows | threejs (aplican igual) | | Hooks: useFrame, useThree, useLoader | r3f | | drei helpers: controls, staging, text | r3f | | CSG con @react-three/csg | r3f |
Quick Start
import { Canvas } from '@react-three/fiber'
import { OrbitControls, Environment } from '@react-three/drei'
function Box() {
return (
<mesh castShadow>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="orange" roughness={0.4} />
</mesh>
)
}
export function Scene() {
return (
<Canvas shadows camera={{ position: [3, 3, 3], fov: 50 }}>
<ambientLight intensity={0.4} />
<directionalLight position={[5, 5, 5]} castShadow />
<Box />
<OrbitControls makeDefault />
<Environment preset="studio" />
</Canvas>
)
}
Learning Path
Nivel 1: Core R3F
- Canvas y Hooks:
references/01-canvas-hooks.md— Canvas props, useThree, useFrame, useLoader, useGraph - JSX y Eventos:
references/02-jsx-system.md— mapeo JSX→Three.js, args, attach, extend, primitive, eventos
Nivel 2: drei Ecosystem
- Controls:
references/03-drei-controls.md— OrbitControls, CameraControls, PivotControls, TransformControls, Keyboard/Scroll - Staging:
references/04-drei-staging.md— Environment, shadows, Sky, materiales especiales - Abstractions:
references/05-drei-abstractions.md— Text, Html, Billboard, Image, Clone, loaders, animaciones
Nivel 3: Performance y Patrones
- Performance:
references/06-drei-performance.md— Bvh, Instances, LOD, PerformanceMonitor, dispose, FBO - Zustand Bridge:
references/07-zustand-integration.md— getState() en useFrame, invalidate(), demand rendering
Nivel 4: Especializado
- CSG Operations:
references/08-csg-operations.md— @react-three/csg, three-bvh-csg, boolean geometry - TypeScript v9:
references/09-typescript-v9.md— ThreeElements, ThreeElement, module augmentation, migracion - Patterns y Gotchas:
references/10-patterns-gotchas.md— anti-patterns, disposal, Suspense, portals
10 Core Rules
- Nunca setState en useFrame — causa re-render a 60fps. Usar refs o
useStore.getState() - useThree con selector —
useThree(s => s.camera), nouseThree()desnudo (re-render en cada cambio de state) - extend() obligatorio para custom objects — v9 no auto-descubre.
extend({ MyClass })antes del JSX - Suspense boundary alrededor de loaders —
useGLTF,useTexture,useLoadersuspenden el componente - frameloop="demand" para escenas estaticas —
invalidate()desde useThree o store para triggerear frames - args como constante —
args={[1,1,1]}inline recrea cada render. Hoistear ouseMemo - dispose={null} para recursos compartidos — R3F auto-dispone en unmount; prevenir con
dispose={null} - makeDefault en controls — registra en
state.controlspara que drei components lo accedan - primitive no clona —
<primitive object={obj}/>usa la misma referencia. Usar<Clone>de drei para reusar - Eventos solo en meshes con geometry —
onClicken<group>sin geometry no dispara
Anti-Patterns
| Anti-pattern | Problema | Fix |
|-------------|---------|-----|
| setState(x) en useFrame | Re-renders 60fps | Refs o Zustand getState() |
| useThree() sin selector | Re-render en cualquier cambio | useThree(s => s.camera) |
| new THREE.Vector3() en render | GC pressure per-frame | Hoistear o useMemo |
| args={[w,h,d]} inline | Reconstruye geometry cada render | useMemo(() => [w,h,d], [w,h,d]) |
| 1000 <mesh> individuales | Reconciliacion lenta | <Instances> de drei |
| useEffect para animar | Fuera del render loop | useFrame con refs |
Checklist Pre-Deploy
[ ] frameloop="demand" si la escena no se anima constantemente
[ ] <Suspense> alrededor de componentes con loaders
[ ] dispose={null} en <primitive> de recursos compartidos
[ ] <Bvh> wrapper si hay muchos meshes clickeables
[ ] dpr={[1, 2]} en Canvas para DPR adaptativo
[ ] OrbitControls con makeDefault
[ ] Sin setState en ningun useFrame
[ ] gl={{ preserveDrawingBuffer: true }} solo si necesitas screenshots
Resources
- R3F Docs: https://r3f.docs.pmnd.rs/
- drei Docs: https://drei.docs.pmnd.rs/
- drei GitHub: https://github.com/pmndrs/drei
- R3F GitHub: https://github.com/pmndrs/react-three-fiber
- three-bvh-csg: https://github.com/gkjohnson/three-bvh-csg