
Threejs Impl React Three Fiber
- 20 installs
- 11 repo stars
- Updated July 8, 2026
- openaec-foundation/three.js-claude-skill-package
Helps with frontend development tasks.
About
threejs-impl-react-three-fiber is a Claude Code skill in the Frontend Development category.
- threejs-impl-react-three-fiber
- Frontend Development
- AI-coding skill
Threejs Impl React Three Fiber by the numbers
- 20 all-time installs (skills.sh)
- +2 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #1,562 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/openaec-foundation/three.js-claude-skill-package --skill threejs-impl-react-three-fiberAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 20 |
|---|---|
| repo stars | ★ 11 |
| Last updated | July 8, 2026 |
| Repository | openaec-foundation/three.js-claude-skill-package ↗ |
What it does
Helps with frontend development tasks.
Files
threejs-impl-react-three-fiber
Quick Reference
Canvas Component Props
| Prop | Type | Default | Purpose |
|---|---|---|---|
gl | `Renderer props \ | (canvas) => Renderer` | {} |
camera | `Camera props \ | THREE.Camera` | { fov: 75, near: 0.1, far: 1000, position: [0,0,5] } |
scene | `Scene props \ | THREE.Scene` | {} |
shadows | `boolean \ | ShadowMapType` | false |
raycaster | Raycaster props | {} | Raycaster configuration |
frameloop | `"always" \ | "demand" \ | "never"` |
resize | ResizeOptions | { scroll: true, debounce: { scroll: 50, resize: 0 } } | Resize behavior |
orthographic | boolean | false | Use OrthographicCamera |
dpr | `number \ | [min, max]` | [1, 2] |
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 |
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:
1. Lowercase JSX = Three.js class. <mesh /> creates new THREE.Mesh(). <meshStandardMaterial /> creates new THREE.MeshStandardMaterial().
2. `args` = constructor arguments (array). <sphereGeometry args={[1, 32, 32]} /> becomes new THREE.SphereGeometry(1, 32, 32). When args changes, the object is destroyed and recreated.
3. `attach` = parent property binding. <meshStandardMaterial attach="material" /> sets parent.material = this. Geometries auto-attach to "geometry", materials to "material".
4. Dash-notation attach for nested paths. attach="shadow-camera" sets parent.shadow.camera = this. Array indexing: attach="material-0".
5. Functional attach. attach={(parent, self) => { parent.add(self); return () => parent.remove(self); }} for custom bind/unbind.
6. Properties with `.set()` accept shorthand. position={[1, 2, 3]} calls object.position.set(1, 2, 3). color="hotpink" calls object.color.set("hotpink").
7. Scalar shorthand. scale={2} calls object.scale.setScalar(2).
8. Dash-case pierces nested properties. rotation-x={Math.PI} sets object.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
- https://r3f.docs.pmnd.rs/
- https://r3f.docs.pmnd.rs/api/canvas
- https://r3f.docs.pmnd.rs/api/hooks
- https://r3f.docs.pmnd.rs/api/events
anti-patterns.md -- React Three Fiber Anti-Patterns
What NOT to do with @react-three/fiber 8.x+. Each anti-pattern includes the mistake, why it fails, and the correct alternative.
---
Anti-Pattern 1: Creating Objects Inside useFrame
WRONG
function BadAnimation() {
useFrame((state) => {
// WRONG: allocates a new Vector3 every frame (60+ times per second)
const target = new THREE.Vector3(
Math.sin(state.clock.elapsedTime),
0,
0
)
meshRef.current.position.copy(target)
})
}WHY IT FAILS
useFrame runs every frame. Creating objects inside it causes thousands of allocations per minute, triggering garbage collection pauses and frame drops.
CORRECT
function GoodAnimation() {
const target = useMemo(() => new THREE.Vector3(), [])
useFrame((state) => {
target.set(Math.sin(state.clock.elapsedTime), 0, 0)
meshRef.current.position.copy(target)
})
}ALWAYS create reusable objects with useMemo or useRef outside useFrame.
---
Anti-Pattern 2: Missing Suspense Boundary for useLoader
WRONG
function Model() {
const gltf = useLoader(GLTFLoader, '/model.glb')
return <primitive object={gltf.scene} />
}
// No Suspense boundary -- crashes with unhandled suspension
function App() {
return (
<Canvas>
<Model />
</Canvas>
)
}WHY IT FAILS
useLoader uses React Suspense internally. Without a <Suspense> boundary, React throws an error because it has no fallback to show during loading.
CORRECT
function App() {
return (
<Canvas>
<Suspense fallback={null}>
<Model />
</Suspense>
</Canvas>
)
}ALWAYS wrap components that use useLoader in <Suspense fallback={...}>.
---
Anti-Pattern 3: Using useThree Without a Selector
WRONG
function CameraLogger() {
// WRONG: subscribes to the ENTIRE state -- re-renders on every frame
const state = useThree()
console.log(state.camera.position)
return null
}WHY IT FAILS
useThree() without a selector returns the full RootState object, which changes on every frame (pointer position, clock, etc.). This causes the component to re-render 60+ times per second unnecessarily.
CORRECT
function CameraLogger() {
// Selector: only re-renders when the camera itself changes
const camera = useThree((state) => state.camera)
console.log(camera.position)
return null
}ALWAYS use a selector function when you only need specific properties from the R3F state.
---
Anti-Pattern 4: Imperative scene.add() Inside R3F
WRONG
function BadMesh() {
const { scene } = useThree()
useEffect(() => {
const mesh = new THREE.Mesh(
new THREE.BoxGeometry(),
new THREE.MeshBasicMaterial({ color: 'red' })
)
scene.add(mesh) // WRONG: bypasses R3F reconciler
return () => {
scene.remove(mesh)
mesh.geometry.dispose()
mesh.material.dispose()
}
}, [scene])
return null
}WHY IT FAILS
R3F manages the scene graph declaratively. Imperatively adding objects via scene.add() bypasses the reconciler, which means:
- R3F does not track the object for events or disposal.
- The object may conflict with R3F's internal state.
- Manual cleanup is error-prone and often missed.
CORRECT
function GoodMesh() {
return (
<mesh>
<boxGeometry />
<meshBasicMaterial color="red" />
</mesh>
)
}ALWAYS use JSX elements for scene objects. R3F handles creation, updates, and disposal automatically.
---
Anti-Pattern 5: Reusing the Same Object Instance in Multiple Primitives
WRONG
function BadDuplication() {
const gltf = useLoader(GLTFLoader, '/model.glb')
return (
<>
{/* WRONG: same object instance appears twice */}
<primitive object={gltf.scene} position={[0, 0, 0]} />
<primitive object={gltf.scene} position={[5, 0, 0]} />
</>
)
}WHY IT FAILS
Three.js objects can only have ONE parent. Adding the same object to two locations silently removes it from the first and places it at the second. The first <primitive> renders nothing.
CORRECT
function GoodDuplication() {
const gltf = useLoader(GLTFLoader, '/model.glb')
// Clone for each instance
const clone1 = useMemo(() => gltf.scene.clone(), [gltf])
const clone2 = useMemo(() => gltf.scene.clone(), [gltf])
return (
<>
<primitive object={clone1} position={[0, 0, 0]} />
<primitive object={clone2} position={[5, 0, 0]} />
</>
)
}ALWAYS clone objects when placing them at multiple positions. Use useMemo to avoid re-cloning on every render.
---
Anti-Pattern 6: Forgetting dispose={null} for Shared Resources
WRONG
const sharedGeometry = new THREE.SphereGeometry(1, 32, 32)
function Particle({ position }) {
return (
<mesh position={position}>
{/* WRONG: R3F will dispose this geometry when ANY Particle unmounts */}
<primitive object={sharedGeometry} attach="geometry" />
<meshBasicMaterial color="white" />
</mesh>
)
}WHY IT FAILS
R3F auto-disposes Three.js objects on unmount. When the first Particle unmounts, sharedGeometry is disposed -- corrupting all remaining particles that reference it.
CORRECT
function Particle({ position }) {
return (
<mesh position={position}>
<primitive object={sharedGeometry} attach="geometry" dispose={null} />
<meshBasicMaterial color="white" />
</mesh>
)
}ALWAYS set dispose={null} on shared resources to prevent premature disposal.
---
Anti-Pattern 7: Hardcoded Time Steps in Animations
WRONG
function BadRotation() {
const meshRef = useRef<THREE.Mesh>(null!)
useFrame(() => {
// WRONG: rotates at different speeds depending on frame rate
meshRef.current.rotation.y += 0.01
})
return <mesh ref={meshRef}><boxGeometry /><meshNormalMaterial /></mesh>
}WHY IT FAILS
A fixed increment per frame means the animation runs faster on 144Hz monitors and slower on 30fps devices. The visual result is inconsistent across hardware.
CORRECT
function GoodRotation() {
const meshRef = useRef<THREE.Mesh>(null!)
useFrame((state, delta) => {
// Consistent rotation speed regardless of frame rate
meshRef.current.rotation.y += delta * 0.5
})
return <mesh ref={meshRef}><boxGeometry /><meshNormalMaterial /></mesh>
}ALWAYS multiply animation increments by delta for frame-rate-independent behavior.
---
Anti-Pattern 8: Not Calling invalidate() in Demand Mode
WRONG
function BadDemandMode() {
return (
<Canvas frameloop="demand">
<mesh
onClick={(e) => {
// WRONG: changes color but never requests a re-render
e.object.material.color.set('red')
}}
>
<boxGeometry />
<meshStandardMaterial />
</mesh>
</Canvas>
)
}WHY IT FAILS
In frameloop="demand" mode, R3F does NOT render continuously. Changing a property without calling invalidate() means the change is never drawn to screen.
CORRECT
function GoodDemandMode() {
const invalidate = useThree((s) => s.invalidate)
return (
<mesh
onClick={(e) => {
e.object.material.color.set('red')
invalidate() // Tell R3F to render the next frame
}}
>
<boxGeometry />
<meshStandardMaterial />
</mesh>
)
}ALWAYS call invalidate() after any visual mutation when using frameloop="demand".
examples.md -- React Three Fiber Working Examples
Verified patterns for @react-three/fiber 8.x+ with React 18+.
---
Example 1: Basic Scene with Animated Mesh
import { Canvas, useFrame } from '@react-three/fiber'
import { useRef } from 'react'
import * as THREE from 'three'
function SpinningBox() {
const meshRef = useRef<THREE.Mesh>(null!)
useFrame((state, delta) => {
// ALWAYS use delta for frame-rate-independent rotation
meshRef.current.rotation.x += delta
meshRef.current.rotation.y += delta * 0.5
})
return (
<mesh ref={meshRef}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="royalblue" />
</mesh>
)
}
export default function App() {
return (
<Canvas camera={{ position: [0, 0, 5] }}>
<ambientLight intensity={0.5} />
<directionalLight position={[5, 5, 5]} />
<SpinningBox />
</Canvas>
)
}Key points:
useFramecallback receivesdelta-- ALWAYS use it for animation timing.meshRefusesnull!assertion for non-null initial value in TypeScript.- Geometry and material are declared as JSX children, auto-attached.
---
Example 2: Loading a GLTF Model with Suspense
import { Canvas, useLoader } from '@react-three/fiber'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { Suspense } from 'react'
// Preload at module scope for instant availability
useLoader.preload(GLTFLoader, '/models/robot.glb')
function Robot() {
const gltf = useLoader(GLTFLoader, '/models/robot.glb')
return <primitive object={gltf.scene} scale={0.5} position={[0, -1, 0]} />
}
export default function App() {
return (
<Canvas>
<ambientLight intensity={0.8} />
{/* ALWAYS wrap useLoader consumers in Suspense */}
<Suspense fallback={null}>
<Robot />
</Suspense>
</Canvas>
)
}Key points:
useLoader.preload()at module scope starts loading before mount.- ALWAYS wrap in
<Suspense>-- without it, the component throws. <primitive>inserts the loaded scene into the R3F tree.
---
Example 3: On-Demand Rendering for Static Scenes
import { Canvas, useThree } from '@react-three/fiber'
import { useEffect } from 'react'
function SceneController() {
const invalidate = useThree((state) => state.invalidate)
useEffect(() => {
// Trigger a re-render after data changes
invalidate()
}, [invalidate])
return null
}
function InteractiveBox() {
const invalidate = useThree((state) => state.invalidate)
return (
<mesh
onClick={(event) => {
event.object.material.color.set('orange')
invalidate() // Request render after color change
}}
>
<boxGeometry args={[2, 2, 2]} />
<meshStandardMaterial color="green" />
</mesh>
)
}
export default function Configurator() {
return (
<Canvas frameloop="demand">
<ambientLight />
<directionalLight position={[3, 3, 3]} />
<InteractiveBox />
<SceneController />
</Canvas>
)
}Key points:
frameloop="demand"stops continuous rendering -- saves GPU.- ALWAYS call
invalidate()after any visual change in demand mode. - Use
useThreewith a selector to avoid unnecessary re-renders.
---
Example 4: Custom Class with extend() and Events
import { Canvas, extend, useFrame } from '@react-three/fiber'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { useRef, useState } from 'react'
import * as THREE from 'three'
// Register OrbitControls as a JSX element
extend({ OrbitControls })
// Declare the JSX intrinsic element for TypeScript
declare global {
namespace JSX {
interface IntrinsicElements {
orbitControls: any
}
}
}
function Controls() {
const { camera, gl } = useThree()
return <orbitControls args={[camera, gl.domElement]} />
}
function HoverableSphere() {
const [hovered, setHovered] = useState(false)
const meshRef = useRef<THREE.Mesh>(null!)
useFrame((state, delta) => {
meshRef.current.rotation.y += delta * 0.3
})
return (
<mesh
ref={meshRef}
onPointerEnter={() => setHovered(true)}
onPointerLeave={() => setHovered(false)}
scale={hovered ? 1.2 : 1}
>
<sphereGeometry args={[1, 32, 32]} />
<meshStandardMaterial color={hovered ? 'hotpink' : 'mediumpurple'} />
</mesh>
)
}
export default function App() {
return (
<Canvas>
<ambientLight intensity={0.5} />
<pointLight position={[10, 10, 10]} />
<HoverableSphere />
<Controls />
</Canvas>
)
}Key points:
extend()MUST be called before using the custom element in JSX.onPointerEnter/onPointerLeavefire once (not continuously likeonPointerOver/onPointerOut).- State changes via
useStatetrigger re-renders and R3F reconciles props.
---
Example 5: Portal for Off-Screen Rendering
import { Canvas, createPortal, useFrame, useThree } from '@react-three/fiber'
import { useMemo, useRef } from 'react'
import * as THREE from 'three'
function MiniMap() {
const { gl, scene, size } = useThree()
const miniMapScene = useMemo(() => new THREE.Scene(), [])
const miniMapCamera = useMemo(
() => new THREE.OrthographicCamera(-5, 5, 5, -5, 0.1, 100),
[]
)
useFrame(() => {
// Render the minimap into a viewport corner
const width = size.width * 0.25
const height = size.height * 0.25
gl.setViewport(0, 0, width, height)
gl.setScissor(0, 0, width, height)
gl.setScissorTest(true)
gl.render(miniMapScene, miniMapCamera)
gl.setScissorTest(false)
gl.setViewport(0, 0, size.width, size.height)
}, 1) // priority > 0: manual rendering
return createPortal(
<>
<ambientLight intensity={1} />
<mesh>
<planeGeometry args={[10, 10]} />
<meshBasicMaterial color="lightgray" />
</mesh>
</>,
miniMapScene
)
}
export default function App() {
return (
<Canvas>
<ambientLight />
<mesh>
<boxGeometry />
<meshNormalMaterial />
</mesh>
<MiniMap />
</Canvas>
)
}Key points:
createPortalrenders children into a separate scene.- Priority > 0 disables auto-render -- the callback MUST render manually.
useMemoprevents recreating the scene and camera on every render.
methods.md -- React Three Fiber API Reference
Complete hook signatures and Canvas API for @react-three/fiber 8.x+.
---
Canvas Component
import { Canvas } from '@react-three/fiber'
<Canvas
gl={rendererProps | factoryFn} // WebGL renderer config
camera={cameraProps | THREE.Camera} // Default camera
scene={sceneProps | THREE.Scene} // Scene config
shadows={boolean | ShadowMapType} // Shadow maps
raycaster={raycasterProps} // Raycaster config
frameloop={"always" | "demand" | "never"} // Render loop
resize={ResizeOptions} // Resize behavior
orthographic={boolean} // Orthographic camera
dpr={number | [min, max]} // Device pixel ratio
linear={boolean} // Linear color space
flat={boolean} // Disable tone mapping
legacy={boolean} // Disable color management
events={EventManager} // Custom event manager
eventSource={HTMLElement | RefObject} // Event capture element
eventPrefix={string} // Coordinate prefix
fallback={ReactNode} // DOM fallback
onCreated={(state: RootState) => void} // Post-init callback
onPointerMissed={(event: PointerEvent) => void} // Miss callback
>
{children}
</Canvas>---
Hooks
useFrame
useFrame(
callback: (state: RootState, delta: number, xrFrame?: XRFrame) => void,
priority?: number
): voidstate-- Full R3F root state (gl, scene, camera, clock, pointer, size, viewport, etc.)delta-- Time in seconds since last frame. ALWAYS use for frame-rate-independent animation.xrFrame-- XR frame object when in WebXR session, undefined otherwise.priority-- Execution order (ascending). When any priority > 0, auto-render is disabled.
useThree
// Full state (triggers re-renders on any state change)
useThree(): RootState
// Selector pattern (triggers re-renders ONLY when selected value changes)
useThree<T>(selector: (state: RootState) => T): TRootState properties:
| Property | Type |
|---|---|
gl | THREE.WebGLRenderer |
scene | THREE.Scene |
camera | THREE.Camera |
raycaster | THREE.Raycaster |
pointer | THREE.Vector2 |
clock | THREE.Clock |
size | { width: number, height: number, top: number, left: number } |
viewport | { width: number, height: number, initialDpr: number, dpr: number, factor: number, distance: number, aspect: number, getCurrentViewport: () => Viewport } |
linear | boolean |
flat | boolean |
legacy | boolean |
frameloop | `"always" \ |
performance | { current: number, min: number, max: number, debounce: number, regress: () => void } |
set | (state: Partial<RootState>) => void |
get | () => RootState |
invalidate | () => void |
advance | (timestamp: number) => void |
setSize | (width: number, height: number) => void |
setDpr | `(dpr: number \ |
setFrameloop | (mode: string) => void |
setEvents | (events: Partial<EventManager>) => void |
events | { connected: boolean, handlers: object, connect: () => void, disconnect: () => void } |
useLoader
// Single URL
useLoader<T>(
loader: new () => Loader<T>,
url: string,
extensions?: (loader: Loader<T>) => void,
onProgress?: (event: ProgressEvent) => void
): T
// Multiple URLs
useLoader<T>(
loader: new () => Loader<T>,
urls: string[],
extensions?: (loader: Loader<T>) => void,
onProgress?: (event: ProgressEvent) => void
): T[]
// Preload (call at module scope or outside components)
useLoader.preload<T>(
loader: new () => Loader<T>,
url: string | string[],
extensions?: (loader: Loader<T>) => void
): void
// Clear cache
useLoader.clear<T>(
loader: new () => Loader<T>,
url: string | string[]
): voiduseGraph
useGraph(
object: THREE.Object3D
): {
nodes: Record<string, THREE.Object3D>,
materials: Record<string, THREE.Material>
}Returns memoized collections keyed by object.name.
---
Utility Functions
extend
import { extend } from '@react-three/fiber'
extend(objects: Record<string, new (...args: any[]) => any>): voidRegisters custom Three.js classes as lowercase JSX elements.
createPortal
import { createPortal } from '@react-three/fiber'
createPortal(
children: React.ReactNode,
container: THREE.Object3D,
state?: Partial<RootState>
): React.ReactNodeRenders children into a different scene/container.
---
JSX Element Props (Universal)
Every R3F JSX element accepts these props:
| Prop | Type | Purpose |
|---|---|---|
args | any[] | Constructor arguments |
attach | `string \ | AttachFn` |
dispose | null | Prevent auto-disposal on unmount |
ref | React.Ref | Access underlying Three.js object |
key | `string \ | number` |
onClick | EventHandler | Click event |
onPointerOver | EventHandler | Pointer enter (continuous) |
onPointerOut | EventHandler | Pointer leave |
onPointerDown | EventHandler | Pointer press |
onPointerUp | EventHandler | Pointer release |
onPointerMove | EventHandler | Pointer move |
onPointerEnter | EventHandler | Pointer enter (once) |
onPointerLeave | EventHandler | Pointer leave (once) |
onDoubleClick | EventHandler | Double click |
onContextMenu | EventHandler | Right-click |
onWheel | EventHandler | Scroll wheel |
onUpdate | (self: Object3D) => void | Called after prop updates |
Event Handler Signature
type EventHandler = (event: ThreeEvent) => void
interface ThreeEvent {
object: THREE.Object3D // Mesh actually hit
eventObject: THREE.Object3D // Object with handler attached
point: THREE.Vector3 // World-space intersection
distance: number // Camera-to-hit distance
uv: THREE.Vector2 // UV at intersection
face: THREE.Face // Intersected face
faceIndex: number // Face index
ray: THREE.Ray // Intersection ray
camera: THREE.Camera // Active camera
intersections: Intersection[] // All hits
delta: number // Pixel distance (down to up)
sourceEvent: Event // Original DOM event
unprojectedPoint: THREE.Vector3
stopPropagation(): void // Stop bubbling
}---
Official Sources
- https://r3f.docs.pmnd.rs/api/canvas
- https://r3f.docs.pmnd.rs/api/hooks
- https://r3f.docs.pmnd.rs/api/events
- https://r3f.docs.pmnd.rs/api/objects