Agent skill
threejs-impl-react-three-fiber
Use when building 3D scenes with React using React Three Fiber (R3F). Prevents the common mistake of mixing imperative Three.js code with R3F's declarative model, creating objects inside useFrame, or forgetting Suspense for loaders. Covers Canvas, useFrame, useThree, useLoader, JSX mapping, event system, performance patterns. Keywords: React Three Fiber, R3F, Canvas, useFrame, useThree, useLoader, @react-three/fiber, 3D React, declarative Three.js.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/threejs-impl-react-three-fiber
Metadata
Additional technical details for this skill
- author
- OpenAEC-Foundation
- version
- 1.0
SKILL.md
threejs-impl-react-three-fiber
Quick Reference
Canvas Component Props
| Prop | Type | Default | Purpose |
|---|---|---|---|
gl |
Renderer props | (canvas) => Renderer |
{} |
WebGL renderer config or factory |
camera |
Camera props | THREE.Camera |
{ fov: 75, near: 0.1, far: 1000, position: [0,0,5] } |
Default camera |
scene |
Scene props | THREE.Scene |
{} |
Scene configuration |
shadows |
boolean | ShadowMapType |
false |
Enable shadow maps |
raycaster |
Raycaster props |
{} |
Raycaster configuration |
frameloop |
"always" | "demand" | "never" |
"always" |
Render loop strategy |
resize |
ResizeOptions |
{ scroll: true, debounce: { scroll: 50, resize: 0 } } |
Resize behavior |
orthographic |
boolean |
false |
Use OrthographicCamera |
dpr |
number | [min, max] |
[1, 2] |
Device pixel ratio |
linear |
boolean |
false |
Linear color space |
flat |
boolean |
false |
Disable tone mapping |
legacy |
boolean |
false |
Disable color management |
events |
EventManager |
R3F default | Custom event manager |
eventSource |
HTMLElement | React.RefObject |
Parent node | Event capture element |
eventPrefix |
string |
"offset" |
Coordinate prefix |
onCreated |
(state: RootState) => void |
-- | Post-init callback |
onPointerMissed |
(event: PointerEvent) => void |
-- | Click misses all meshes |
fallback |
React.ReactNode |
-- | DOM fallback during init |
Frameloop Modes
| Mode | Behavior |
|---|---|
"always" |
ALWAYS renders every frame via requestAnimationFrame |
"demand" |
ONLY renders when invalidate() is called -- use for static scenes |
"never" |
NEVER renders automatically -- caller MUST invoke advance(timestamp) |
Critical Warnings
NEVER create Three.js objects inside useFrame -- this allocates memory every frame and causes GC pressure. ALWAYS create objects outside the callback or use useMemo.
NEVER forget <Suspense> when using useLoader -- the component WILL suspend and crash without a Suspense boundary.
NEVER add the same Three.js object instance to the scene tree multiple times via <primitive> -- Three.js objects can only have one parent.
NEVER use useThree() without a selector when you only need one property -- the full state object triggers re-renders on every frame. ALWAYS use useThree((s) => s.camera).
NEVER mix imperative scene.add() calls with R3F's declarative JSX tree -- R3F manages the scene graph and imperative mutations cause desync.
ALWAYS use delta from useFrame for animations -- hardcoded time steps cause speed variations across different frame rates.
ALWAYS use useMemo for imperatively created geometries and materials -- without it, new objects are allocated on every render.
JSX-to-Three.js Mapping Rules
R3F uses deterministic conventions to translate JSX into Three.js scene graph operations:
-
Lowercase JSX = Three.js class.
<mesh />createsnew THREE.Mesh().<meshStandardMaterial />createsnew THREE.MeshStandardMaterial(). -
args= constructor arguments (array).<sphereGeometry args={[1, 32, 32]} />becomesnew THREE.SphereGeometry(1, 32, 32). Whenargschanges, the object is destroyed and recreated. -
attach= parent property binding.<meshStandardMaterial attach="material" />setsparent.material = this. Geometries auto-attach to"geometry", materials to"material". -
Dash-notation attach for nested paths.
attach="shadow-camera"setsparent.shadow.camera = this. Array indexing:attach="material-0". -
Functional attach.
attach={(parent, self) => { parent.add(self); return () => parent.remove(self); }}for custom bind/unbind. -
Properties with
.set()accept shorthand.position={[1, 2, 3]}callsobject.position.set(1, 2, 3).color="hotpink"callsobject.color.set("hotpink"). -
Scalar shorthand.
scale={2}callsobject.scale.setScalar(2). -
Dash-case pierces nested properties.
rotation-x={Math.PI}setsobject.rotation.x = Math.PI.
Hooks
useFrame
useFrame((state: RootState, delta: number, xrFrame?: XRFrame) => void, priority?: number)
Subscribes a callback to the render loop. Executes every frame.
State object key properties:
| Property | Type | Description |
|---|---|---|
gl |
THREE.WebGLRenderer |
The renderer |
scene |
THREE.Scene |
The scene |
camera |
THREE.Camera |
Active camera |
clock |
THREE.Clock |
System clock |
pointer |
THREE.Vector2 |
Normalized pointer (-1 to +1) |
size |
{ width, height, top, left } |
Canvas dimensions (px) |
viewport |
{ width, height, factor, distance, aspect } |
Camera-relative metrics |
invalidate |
() => void |
Request render in demand mode |
advance |
(timestamp: number) => void |
Advance one tick in never mode |
performance |
{ current, min, max, regress() } |
Adaptive performance |
set |
(state) => void |
Mutate state directly |
get |
() => RootState |
Read state non-reactively |
Priority system: Callbacks execute in ascending priority order. When ANY callback has priority > 0, R3F disables automatic renderer.render(). The highest-priority subscriber MUST call state.gl.render(state.scene, state.camera) manually. Negative priorities do NOT disable auto-rendering.
useThree
const state = useThree() // full state (re-renders often)
const camera = useThree((state) => state.camera) // selector (re-renders only on change)
Returns the RootState (same object as useFrame's state). ALWAYS use a selector when only one property is needed.
useLoader
const result = useLoader(LoaderClass, url, extensions?, onProgress?)
const results = useLoader(LoaderClass, [url1, url2], extensions?)
Suspense-based asset loading. ALWAYS wrap in <Suspense fallback={...}>.
- Assets are cached by URL -- loading the same URL twice returns the cached result.
useLoader.preload(LoaderClass, url)preloads before component mount.- GLTF results include
{ nodes, materials, scene, animations }.
useGraph
const { nodes, materials } = useGraph(object3D)
Traverses an Object3D hierarchy and returns memoized { nodes, materials } collections keyed by name.
Primitives and extend()
Primitives
Insert pre-existing Three.js objects into the declarative tree:
<primitive object={existingMesh} position={[10, 0, 0]} />
- NEVER add the same object instance multiple times -- Three.js objects can have only one parent.
- Primitives do NOT auto-dispose; the caller MUST manage lifecycle.
extend()
Register custom Three.js classes as JSX elements:
import { extend } from '@react-three/fiber'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
extend({ OrbitControls })
// Now usable as <orbitControls args={[camera, domElement]} />
The JSX element name is the camelCase version of the registered key.
Event System
R3F implements pointer events via raycasting. Events bubble through the scene graph.
Supported Events
| Event | Trigger |
|---|---|
onClick |
Pointer click on mesh |
onContextMenu |
Right-click / context menu |
onDoubleClick |
Double click |
onPointerUp |
Pointer released |
onPointerDown |
Pointer pressed |
onPointerOver |
Pointer enters mesh (fires continuously) |
onPointerOut |
Pointer leaves mesh |
onPointerEnter |
Pointer enters mesh (fires once) |
onPointerLeave |
Pointer leaves mesh (fires once) |
onPointerMove |
Pointer moves over mesh |
onPointerMissed |
Click hits no mesh (Canvas-level) |
onWheel |
Scroll wheel |
onUpdate |
Object receives new props |
Event Object Properties
| Property | Type | Description |
|---|---|---|
object |
THREE.Object3D |
The mesh actually hit |
eventObject |
THREE.Object3D |
Object with the event handler |
point |
THREE.Vector3 |
Intersection in world space |
distance |
number |
Camera-to-intersection distance |
uv |
THREE.Vector2 |
UV coordinates at intersection |
face |
THREE.Face |
Intersected face |
ray |
THREE.Ray |
Ray used for intersection |
camera |
THREE.Camera |
Active camera |
intersections |
Intersection[] |
All intersected objects |
delta |
number |
Pixel distance down-to-up |
sourceEvent |
Event |
Original DOM event |
stopPropagation() |
function |
Prevent bubbling to occluded objects |
Performance Patterns
Disposal
- R3F ALWAYS calls
dispose()on Three.js objects when components unmount, freeing GPU resources. - Set
dispose={null}on an element to PREVENT auto-disposal -- use when objects are shared across components.
Static Scenes
Use frameloop="demand" with invalidate() for scenes that do not animate continuously (dashboards, configurators). This saves GPU cycles.
Portals
import { createPortal } from '@react-three/fiber'
createPortal(children, targetScene)
Renders children into a different scene/layer without affecting the main scene graph.
useMemo for Imperative Objects
ALWAYS wrap imperatively created geometries and materials in useMemo:
const geometry = useMemo(() => new THREE.TorusKnotGeometry(1, 0.3, 128, 32), [])
Without useMemo, a new object is created on every render.
Reference Links
- references/methods.md -- Hook signatures and Canvas API
- references/examples.md -- Working R3F code examples
- references/anti-patterns.md -- What NOT to do with R3F
Official Sources
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?