---
name: r3f-fundamentals
description: React Three Fiber fundamentals - Canvas, hooks (useFrame, useThree), JSX elements, events, refs. Use when setting up R3F scenes, creating components, handling the render loop, or working with Three.js objects in React.
---
# React Three Fiber Fundamentals
## Quick Start
```tsx
import { Canvas } from '@react-three/fiber'
import { useRef } from 'react'
import { useFrame } from '@react-three/fiber'
function RotatingBox() {
const meshRef = useRef()
useFrame((state, delta) => {
meshRef.current.rotation.x += delta
meshRef.current.rotation.y += delta * 0.5
})
return (
)
}
export default function App() {
return (
)
}
```
## Canvas Component
The root component that creates the WebGL context, scene, camera, and renderer.
```tsx
import { Canvas } from '@react-three/fiber'
function App() {
return (
)
}
```
### Canvas Defaults
R3F sets sensible defaults:
- Renderer: antialias, alpha, outputColorSpace = SRGBColorSpace
- Camera: PerspectiveCamera at [0, 0, 5]
- Scene: Automatic resize handling
- Events: Pointer events enabled
## useFrame Hook
Subscribe to the render loop. Called every frame (typically 60fps).
```tsx
import { useFrame } from '@react-three/fiber'
import { useRef } from 'react'
function AnimatedMesh() {
const meshRef = useRef()
useFrame((state, delta, xrFrame) => {
// state: Full R3F state (see useThree)
// delta: Time since last frame in seconds
// xrFrame: XR frame if in VR/AR mode
// Animate rotation
meshRef.current.rotation.y += delta
// Access clock
const elapsed = state.clock.elapsedTime
meshRef.current.position.y = Math.sin(elapsed) * 2
// Access pointer position (-1 to 1)
const { x, y } = state.pointer
meshRef.current.rotation.x = y * 0.5
meshRef.current.rotation.z = x * 0.5
})
return (
)
}
```
### useFrame with Priority
Control render order with priority (higher = later).
```tsx
// Default priority is 0
useFrame((state, delta) => {
// Runs first
}, -1)
useFrame((state, delta) => {
// Runs after priority -1
}, 0)
// Manual rendering with positive priority
useFrame((state, delta) => {
// Take over rendering
state.gl.render(state.scene, state.camera)
}, 1)
```
### Conditional useFrame
```tsx
function ConditionalAnimation({ active }) {
useFrame((state, delta) => {
if (!active) return // Skip when inactive
meshRef.current.rotation.y += delta
})
}
```
## useThree Hook
Access the R3F state store.
```tsx
import { useThree } from '@react-three/fiber'
function CameraInfo() {
// Get full state (triggers re-render on any change)
const state = useThree()
// Selective subscription (recommended)
const camera = useThree((state) => state.camera)
const gl = useThree((state) => state.gl)
const scene = useThree((state) => state.scene)
const size = useThree((state) => state.size)
// Available state properties:
// gl: WebGLRenderer
// scene: Scene
// camera: Camera
// raycaster: Raycaster
// pointer: Vector2 (normalized -1 to 1)
// mouse: Vector2 (deprecated, use pointer)
// clock: Clock
// size: { width, height, top, left }
// viewport: { width, height, factor, distance, aspect }
// performance: { current, min, max, debounce, regress }
// events: Event handlers
// set: State setter
// get: State getter
// invalidate: Trigger re-render (for frameloop="demand")
// advance: Advance one frame (for frameloop="never")
return null
}
```
### Common useThree Patterns
```tsx
// Responsive to viewport
function ResponsiveObject() {
const viewport = useThree((state) => state.viewport)
return (
)
}
// Manual render trigger
function TriggerRender() {
const invalidate = useThree((state) => state.invalidate)
const handleClick = () => {
// Trigger render when using frameloop="demand"
invalidate()
}
}
// Update camera
function CameraController() {
const camera = useThree((state) => state.camera)
const set = useThree((state) => state.set)
useEffect(() => {
camera.position.set(10, 10, 10)
camera.lookAt(0, 0, 0)
}, [camera])
}
```
## JSX Elements
All Three.js objects are available as JSX elements (camelCase).
### Meshes
```tsx
// Basic mesh structure
// With ref
const meshRef = useRef()
// meshRef.current is the THREE.Mesh
```
### Geometry args
Constructor arguments via `args` prop:
```tsx
// BoxGeometry(width, height, depth, widthSegments, heightSegments, depthSegments)
// SphereGeometry(radius, widthSegments, heightSegments)
// PlaneGeometry(width, height, widthSegments, heightSegments)
// CylinderGeometry(radiusTop, radiusBottom, height, radialSegments)
```
### Groups
```tsx
```
### Nested Properties
Use dashes for nested properties:
```tsx
// Shadow camera properties
```
### attach Prop
Control how children attach to parents:
```tsx
{/* Default: attaches as 'material' */}
{/* Explicit attach */}
{/* Array attachment */}
{/* Custom attachment with function */}
{
parent.map = self
return () => { parent.map = null } // Cleanup
}}
/>
```
## Event Handling
R3F provides React-style events on 3D objects.
```tsx
function InteractiveBox() {
const [hovered, setHovered] = useState(false)
const [clicked, setClicked] = useState(false)
return (
{
e.stopPropagation() // Prevent bubbling
setClicked(!clicked)
// Event properties:
console.log(e.object) // THREE.Mesh
console.log(e.point) // Vector3 - intersection point
console.log(e.distance) // Distance from camera
console.log(e.face) // Intersected face
console.log(e.faceIndex) // Face index
console.log(e.uv) // UV coordinates
console.log(e.normal) // Face normal
console.log(e.pointer) // Normalized pointer coords
console.log(e.ray) // Raycaster ray
console.log(e.camera) // Camera
console.log(e.delta) // Distance moved (drag events)
}}
onContextMenu={(e) => console.log('Right click')}
onDoubleClick={(e) => console.log('Double click')}
onPointerOver={(e) => {
e.stopPropagation()
setHovered(true)
document.body.style.cursor = 'pointer'
}}
onPointerOut={(e) => {
setHovered(false)
document.body.style.cursor = 'default'
}}
onPointerDown={(e) => console.log('Pointer down')}
onPointerUp={(e) => console.log('Pointer up')}
onPointerMove={(e) => console.log('Moving over mesh')}
onWheel={(e) => console.log('Wheel:', e.deltaY)}
scale={hovered ? 1.2 : 1}
>
)
}
```
### Event Propagation
Events bubble up through the scene graph:
```tsx
console.log('Group clicked')}>
{
e.stopPropagation() // Stop bubbling to group
console.log('Mesh clicked')
}}>
```
## primitive Element
Use existing Three.js objects directly:
```tsx
import * as THREE from 'three'
// Existing object
const geometry = new THREE.BoxGeometry()
const material = new THREE.MeshStandardMaterial({ color: 'red' })
const mesh = new THREE.Mesh(geometry, material)
function Scene() {
return
}
// Common with loaded models
function Model({ gltf }) {
return
}
```
## extend Function
Register custom Three.js classes for JSX use:
```tsx
import { extend } from '@react-three/fiber'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
// Extend once (usually at module level)
extend({ OrbitControls })
// Now use as JSX
function Scene() {
const { camera, gl } = useThree()
return
}
// TypeScript declaration
declare global {
namespace JSX {
interface IntrinsicElements {
orbitControls: ReactThreeFiber.Object3DNode
}
}
}
```
## Refs and Imperative Access
```tsx
import { useRef, useEffect } from 'react'
import { useFrame } from '@react-three/fiber'
import * as THREE from 'three'
function MeshWithRef() {
const meshRef = useRef(null)
const materialRef = useRef(null)
useEffect(() => {
if (meshRef.current) {
// Direct Three.js access
meshRef.current.geometry.computeBoundingBox()
console.log(meshRef.current.geometry.boundingBox)
}
}, [])
useFrame(() => {
if (materialRef.current) {
materialRef.current.color.setHSL(Math.random(), 1, 0.5)
}
})
return (
)
}
```
## Performance Patterns
### Avoiding Re-renders
```tsx
// BAD: Creates new object every render
// GOOD: Mutate existing position
const meshRef = useRef()
useFrame(() => {
meshRef.current.position.x = x
})
// GOOD: Use useMemo for static values
const position = useMemo(() => [x, y, z], [x, y, z])
```
### Component Isolation
```tsx
// Isolate animated components to prevent parent re-renders
function Scene() {
return (
<>
{/* Only this re-renders on animation */}
>
)
}
function AnimatedObject() {
const ref = useRef()
useFrame((_, delta) => {
ref.current.rotation.y += delta
})
return
}
```
### Dispose
R3F auto-disposes geometries, materials, and textures. Override with:
```tsx
{/* Prevent auto-dispose */}
```
## Common Patterns
### Fullscreen Canvas
```tsx
// styles.css
html, body, #root {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
// App.tsx