r3f-best-practices
229
总安装量
206
周安装量
#1205
全站排名
安装命令
npx skills add https://github.com/emalorenzo/three-agent-skills --skill r3f-best-practices
Agent 安装分布
claude-code
148
opencode
133
gemini-cli
129
cursor
109
github-copilot
105
Skill 文档
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} />