Claude
Skills
Sign in
Back

r3f-fundamentals

Included with Lifetime
$97 forever

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.

Image & Video

What this skill does


# 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 (
    <mesh ref={meshRef}>
      <boxGeometry args={[1, 1, 1]} />
      <meshStandardMaterial color="hotpink" />
    </mesh>
  )
}

export default function App() {
  return (
    <Canvas camera={{ position: [0, 0, 5], fov: 75 }}>
      <ambientLight intensity={0.5} />
      <directionalLight position={[5, 5, 5]} />
      <RotatingBox />
    </Canvas>
  )
}
```

## 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
      // Camera configuration
      camera={{
        position: [0, 5, 10],
        fov: 75,
        near: 0.1,
        far: 1000,
      }}
      // Or use orthographic
      orthographic
      camera={{ zoom: 50, position: [0, 0, 100] }}

      // Renderer settings
      gl={{
        antialias: true,
        alpha: true,
        powerPreference: 'high-performance',
        preserveDrawingBuffer: true,  // For screenshots
      }}
      dpr={[1, 2]}  // Pixel ratio min/max

      // Shadows
      shadows  // or shadows="soft" | "basic" | "percentage"

      // Color management
      flat  // Disable automatic sRGB color management

      // Frame loop control
      frameloop="demand"  // 'always' | 'demand' | 'never'

      // Event handling
      eventSource={document.getElementById('root')}
      eventPrefix="client"  // 'offset' | 'client' | 'page' | 'layer' | 'screen'

      // Callbacks
      onCreated={(state) => {
        console.log('Canvas ready:', state.gl, state.scene, state.camera)
      }}
      onPointerMissed={() => console.log('Clicked background')}

      // Styling
      style={{ width: '100%', height: '100vh' }}
    >
      <Scene />
    </Canvas>
  )
}
```

### 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 (
    <mesh ref={meshRef}>
      <boxGeometry />
      <meshStandardMaterial color="orange" />
    </mesh>
  )
}
```

### 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 (
    <mesh scale={[viewport.width / 4, viewport.height / 4, 1]}>
      <planeGeometry />
      <meshBasicMaterial color="blue" />
    </mesh>
  )
}

// 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
<mesh
  position={[0, 0, 0]}       // x, y, z
  rotation={[0, Math.PI, 0]} // Euler angles in radians
  scale={[1, 2, 1]}          // x, y, z or single number
  visible={true}
  castShadow
  receiveShadow
>
  <boxGeometry args={[1, 1, 1]} />
  <meshStandardMaterial color="red" />
</mesh>

// With ref
const meshRef = useRef()
<mesh ref={meshRef} />
// meshRef.current is the THREE.Mesh
```

### Geometry args

Constructor arguments via `args` prop:

```tsx
// BoxGeometry(width, height, depth, widthSegments, heightSegments, depthSegments)
<boxGeometry args={[1, 1, 1, 1, 1, 1]} />

// SphereGeometry(radius, widthSegments, heightSegments)
<sphereGeometry args={[1, 32, 32]} />

// PlaneGeometry(width, height, widthSegments, heightSegments)
<planeGeometry args={[10, 10]} />

// CylinderGeometry(radiusTop, radiusBottom, height, radialSegments)
<cylinderGeometry args={[1, 1, 2, 32]} />
```

### Groups

```tsx
<group position={[5, 0, 0]} rotation={[0, Math.PI / 4, 0]}>
  <mesh position={[-1, 0, 0]}>
    <boxGeometry />
    <meshStandardMaterial color="red" />
  </mesh>
  <mesh position={[1, 0, 0]}>
    <boxGeometry />
    <meshStandardMaterial color="blue" />
  </mesh>
</group>
```

### Nested Properties

Use dashes for nested properties:

```tsx
<mesh
  position-x={5}
  rotation-y={Math.PI}
  scale-z={2}
>
  <meshStandardMaterial
    color="red"
    metalness={0.8}
    roughness={0.2}
  />
</mesh>

// Shadow camera properties
<directionalLight
  castShadow
  shadow-mapSize={[2048, 2048]}
  shadow-camera-left={-10}
  shadow-camera-right={10}
  shadow-camera-top={10}
  shadow-camera-bottom={-10}
/>
```

### attach Prop

Control how children attach to parents:

```tsx
<mesh>
  <boxGeometry />
  {/* Default: attaches as 'material' */}
  <meshStandardMaterial />
</mesh>

{/* Explicit attach */}
<mesh>
  <boxGeometry attach="geometry" />
  <meshStandardMaterial attach="material" />
</mesh>

{/* Array attachment */}
<mesh>
  <boxGeometry />
  <meshStandardMaterial attach="material-0" color="red" />
  <meshStandardMaterial attach="material-1" color="blue" />
</mesh>

{/* Custom attachment with function */}
<someObject>
  <texture
    attach={(parent, self) => {
      parent.map = self
      return () => { parent.map = null }  // Cl
Files: 1
Size: 16.7 KB
Complexity: 21/100
Category: Image & Video

Related in Image & Video