--- name: r3f-best-practices description: React Three Fiber (R3F) and Poimandres ecosystem best practices. Use when writing, reviewing, or optimizing R3F code. Triggers on tasks involving @react-three/fiber, @react-three/drei, zustand, @react-three/postprocessing, @react-three/rapier, or leva. license: MIT metadata: author: three-agent-skills version: "1.0.0" --- # React Three Fiber Best Practices Comprehensive guide for React Three Fiber and the Poimandres ecosystem. Contains 60+ rules across 11 categories, prioritized by impact. ## When to Apply Reference these guidelines when: - Writing new R3F components - Optimizing R3F performance (re-renders are the #1 issue) - Using Drei helpers correctly - Managing state with Zustand - Implementing post-processing or physics ## Ecosystem Coverage - **@react-three/fiber** - React renderer for Three.js - **@react-three/drei** - Useful helpers and abstractions - **@react-three/postprocessing** - Post-processing effects - **@react-three/rapier** - Physics engine - **zustand** - State management - **leva** - Debug GUI ## Rule Categories by Priority | Priority | Category | Impact | Prefix | |----------|----------|--------|--------| | 1 | Performance & Re-renders | CRITICAL | `perf-` | | 2 | useFrame & Animation | CRITICAL | `frame-` | | 3 | Component Patterns | HIGH | `component-` | | 4 | Canvas & Setup | HIGH | `canvas-` | | 5 | Drei Helpers | MEDIUM-HIGH | `drei-` | | 6 | Loading & Suspense | MEDIUM-HIGH | `loading-` | | 7 | State Management | MEDIUM | `state-` | | 8 | Events & Interaction | MEDIUM | `events-` | | 9 | Post-processing | MEDIUM | `postpro-` | | 10 | Physics (Rapier) | LOW-MEDIUM | `physics-` | | 11 | Leva (Debug GUI) | LOW | `leva-` | ## Quick Reference ### 1. Performance & Re-renders (CRITICAL) - `perf-never-set-state-in-useframe` - NEVER call setState in useFrame - `perf-isolate-state` - Isolate components that need React state - `perf-zustand-selectors` - Use Zustand selectors, not entire store - `perf-transient-subscriptions` - Use transient subscriptions for continuous values - `perf-memo-components` - Memoize expensive components - `perf-keys-for-lists` - Use stable keys for dynamic lists - `perf-avoid-inline-objects` - Avoid creating objects/arrays in JSX - `perf-dispose-auto` - Understand R3F auto-dispose behavior ### 2. useFrame & Animation (CRITICAL) - `frame-priority` - Use priority for execution order - `frame-delta-time` - Always use delta for animations - `frame-conditional-subscription` - Disable useFrame when not needed - `frame-destructure-state` - Destructure only what you need - `frame-render-on-demand` - Use invalidate() for on-demand rendering - `frame-avoid-heavy-computation` - Move heavy work outside useFrame ### 3. Component Patterns (HIGH) - `component-jsx-elements` - Use JSX for Three.js objects - `component-attach-prop` - Use attach for non-standard properties - `component-primitive` - Use primitive for existing objects - `component-extend` - Use extend() for custom classes - `component-forwardref` - Use forwardRef for reusable components - `component-dispose-null` - Set dispose={null} on shared resources ### 4. Canvas & Setup (HIGH) - `canvas-size-container` - Canvas fills parent container - `canvas-camera-default` - Configure camera via prop - `canvas-gl-config` - Configure WebGL context - `canvas-shadows` - Enable shadows at Canvas level - `canvas-frameloop` - Choose appropriate frameloop mode - `canvas-events` - Configure event handling - `canvas-linear-flat` - Use linear/flat for correct colors ### 5. Drei Helpers (MEDIUM-HIGH) - `drei-use-gltf` - useGLTF with preloading - `drei-use-texture` - useTexture for texture loading - `drei-environment` - Environment for realistic lighting - `drei-orbit-controls` - OrbitControls from Drei - `drei-html` - Html for DOM overlays - `drei-text` - Text for 3D text - `drei-instances` - Instances for optimized instancing - `drei-use-helper` - useHelper for debug visualization - `drei-bounds` - Bounds to fit camera - `drei-center` - Center to center objects - `drei-float` - Float for floating animation ### 6. Loading & Suspense (MEDIUM-HIGH) - `loading-suspense` - Wrap async components in Suspense - `loading-preload` - Preload assets with useGLTF.preload - `loading-use-progress` - useProgress for loading UI - `loading-lazy-components` - Lazy load heavy components - `loading-error-boundary` - Handle loading errors ### 7. State Management (MEDIUM) - `state-zustand-store` - Create focused Zustand stores - `state-avoid-objects-in-store` - Be careful with Three.js objects - `state-subscribeWithSelector` - Fine-grained subscriptions - `state-persist` - Persist state when needed - `state-separate-concerns` - Separate stores by concern ### 8. Events & Interaction (MEDIUM) - `events-pointer-events` - Use pointer events on meshes - `events-stop-propagation` - Prevent event bubbling - `events-cursor-pointer` - Change cursor on hover - `events-raycast-filter` - Filter raycasting - `events-event-data` - Understand event data structure ### 9. Post-processing (MEDIUM) - `postpro-effect-composer` - Use EffectComposer - `postpro-common-effects` - Common effects reference - `postpro-selective-bloom` - SelectiveBloom for optimized glow - `postpro-custom-shader` - Create custom effects - `postpro-performance` - Optimize post-processing ### 10. Physics Rapier (LOW-MEDIUM) - `physics-setup` - Basic Rapier setup - `physics-body-types` - dynamic, fixed, kinematic - `physics-colliders` - Choose appropriate colliders - `physics-events` - Handle collision events - `physics-api-ref` - Use ref for physics API - `physics-performance` - Optimize physics ### 11. Leva (LOW) - `leva-basic` - Basic Leva usage - `leva-folders` - Organize with folders - `leva-conditional` - Hide in production ## How to Use Read individual rule files for detailed explanations and code examples: ``` rules/perf-never-set-state-in-useframe.md rules/drei-use-gltf.md rules/state-zustand-selectors.md ``` ## Full Compiled Document For the complete guide with all rules expanded: `../R3F_BEST_PRACTICES.md` ## Critical Patterns ### NEVER setState in useFrame ```jsx // BAD - 60 re-renders per second! function BadComponent() { const [position, setPosition] = useState(0); useFrame(() => { setPosition(p => p + 0.01); // NEVER DO THIS }); return ; } // GOOD - Mutate refs directly function GoodComponent() { const meshRef = useRef(); useFrame(() => { meshRef.current.position.x += 0.01; }); return ; } ``` ### Zustand Selectors ```jsx // BAD - Re-renders on ANY store change const store = useGameStore(); // GOOD - Only re-renders when playerX changes const playerX = useGameStore(state => state.playerX); // BETTER - No re-renders, direct mutation useFrame(() => { const { value } = useStore.getState(); ref.current.position.x = value; }); ``` ### Drei useGLTF ```jsx import { useGLTF } from '@react-three/drei'; function Model() { const { scene } = useGLTF('/model.glb'); return ; } // Preload for instant loading useGLTF.preload('/model.glb'); ``` ### Suspense Loading ```jsx function App() { return ( }> ); } ```