r3f-best-practices
Installation
Summary
Comprehensive guide for optimizing React Three Fiber and Poimandres ecosystem code across 70+ rules.
- Covers 12 priority-ranked categories from performance and re-renders (critical) through physics and debug tools, with rule prefixes for quick reference
- Emphasizes avoiding setState in useFrame, isolating React state, using Zustand selectors, and memoizing expensive components to prevent excessive re-renders
- Includes patterns for useFrame animation with delta time, Drei helpers (useGLTF, useTexture, Environment), Suspense loading, and post-processing effects
- Provides code examples for common pitfalls: direct ref mutation instead of state updates, visibility toggling instead of remounting, and transient subscriptions for continuous values
SKILL.md
React Three Fiber Best Practices
Comprehensive guide for React Three Fiber and the Poimandres ecosystem. Contains 70+ rules across 12 categories, prioritized by impact.
Sources & Credits
Additional tips from 100 Three.js Tips by Utsubo
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 useFrameperf-isolate-state- Isolate components that need React stateperf-zustand-selectors- Use Zustand selectors, not entire storeperf-transient-subscriptions- Use transient subscriptions for continuous valuesperf-memo-components- Memoize expensive componentsperf-keys-for-lists- Use stable keys for dynamic listsperf-avoid-inline-objects- Avoid creating objects/arrays in JSXperf-dispose-auto- Understand R3F auto-dispose behaviorperf-visibility-toggle- Toggle visibility instead of remountingperf-r3f-perf- Use r3f-perf for performance monitoring
2. useFrame & Animation (CRITICAL)
frame-priority- Use priority for execution orderframe-delta-time- Always use delta for animationsframe-conditional-subscription- Disable useFrame when not neededframe-destructure-state- Destructure only what you needframe-render-on-demand- Use invalidate() for on-demand renderingframe-avoid-heavy-computation- Move heavy work outside useFrame
3. Component Patterns (HIGH)
component-jsx-elements- Use JSX for Three.js objectscomponent-attach-prop- Use attach for non-standard propertiescomponent-primitive- Use primitive for existing objectscomponent-extend- Use extend() for custom classescomponent-forwardref- Use forwardRef for reusable componentscomponent-dispose-null- Set dispose={null} on shared resources
4. Canvas & Setup (HIGH)
canvas-size-container- Canvas fills parent containercanvas-camera-default- Configure camera via propcanvas-gl-config- Configure WebGL contextcanvas-shadows- Enable shadows at Canvas levelcanvas-frameloop- Choose appropriate frameloop modecanvas-events- Configure event handlingcanvas-linear-flat- Use linear/flat for correct colors
5. Drei Helpers (MEDIUM-HIGH)
drei-use-gltf- useGLTF with preloadingdrei-use-texture- useTexture for texture loadingdrei-environment- Environment for realistic lightingdrei-orbit-controls- OrbitControls from Dreidrei-html- Html for DOM overlaysdrei-text- Text for 3D textdrei-instances- Instances for optimized instancingdrei-use-helper- useHelper for debug visualizationdrei-bounds- Bounds to fit cameradrei-center- Center to center objectsdrei-float- Float for floating animation
6. Loading & Suspense (MEDIUM-HIGH)
loading-suspense- Wrap async components in Suspenseloading-preload- Preload assets with useGLTF.preloadloading-use-progress- useProgress for loading UIloading-lazy-components- Lazy load heavy componentsloading-error-boundary- Handle loading errors
7. State Management (MEDIUM)
state-zustand-store- Create focused Zustand storesstate-avoid-objects-in-store- Be careful with Three.js objectsstate-subscribeWithSelector- Fine-grained subscriptionsstate-persist- Persist state when neededstate-separate-concerns- Separate stores by concern
8. Events & Interaction (MEDIUM)
events-pointer-events- Use pointer events on meshesevents-stop-propagation- Prevent event bubblingevents-cursor-pointer- Change cursor on hoverevents-raycast-filter- Filter raycastingevents-event-data- Understand event data structure
9. Post-processing (MEDIUM)
postpro-effect-composer- Use EffectComposerpostpro-common-effects- Common effects referencepostpro-selective-bloom- SelectiveBloom for optimized glowpostpro-custom-shader- Create custom effectspostpro-performance- Optimize post-processing
10. Physics Rapier (LOW-MEDIUM)
physics-setup- Basic Rapier setupphysics-body-types- dynamic, fixed, kinematicphysics-colliders- Choose appropriate collidersphysics-events- Handle collision eventsphysics-api-ref- Use ref for physics APIphysics-performance- Optimize physics
11. Leva (LOW)
leva-basic- Basic Leva usageleva-folders- Organize with foldersleva-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
// BAD - 60 re-renders per second!
function BadComponent() {
const [position, setPosition] = useState(0);
useFrame(() => {
setPosition(p => p + 0.01); // NEVER DO THIS
});
return <mesh position-x={position} />;
}
// GOOD - Mutate refs directly
function GoodComponent() {
const meshRef = useRef();
useFrame(() => {
meshRef.current.position.x += 0.01;
});
return <mesh ref={meshRef} />;
}
Zustand Selectors
// 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
import { useGLTF } from '@react-three/drei';
function Model() {
const { scene } = useGLTF('/model.glb');
return <primitive object={scene} />;
}
// Preload for instant loading
useGLTF.preload('/model.glb');
Suspense Loading
function App() {
return (
<Canvas>
<Suspense fallback={<Loader />}>
<Model />
</Suspense>
</Canvas>
);
}
r3f-perf Monitoring
import { Perf } from 'r3f-perf';
function App() {
return (
<Canvas>
<Perf position="top-left" />
<Scene />
</Canvas>
);
}
Toggle Visibility (Not Remounting)
// BAD: Remounting destroys and recreates
{showModel && <Model />}
// GOOD: Toggle visibility, keeps instance alive
<Model visible={showModel} />