# React Three Fiber

> Build declarative 3D scenes with React Three Fiber (R3F) - a React renderer for Three.js. Use when building interactive 3D experiences in React applications with component-based architecture, state management, and reusable abstractions. Ideal for product configurators, portfolios, games, data visualization, and immersive web experiences.

- **Type:** Skill
- **Install:** `agentstack add skill-freshtechbro-claudedesignskills-react-three-fiber`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [freshtechbro](https://agentstack.voostack.com/s/freshtechbro)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [freshtechbro](https://github.com/freshtechbro)
- **Source:** https://github.com/freshtechbro/claudedesignskills/tree/main/.factory/skills/react-three-fiber

## Install

```sh
agentstack add skill-freshtechbro-claudedesignskills-react-three-fiber
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# React Three Fiber

## Overview

React Three Fiber (R3F) is a React renderer for Three.js that brings declarative, component-based 3D development to React applications. Instead of imperatively creating and managing Three.js objects, you build 3D scenes using JSX components that map directly to Three.js objects.

**When to Use This Skill**:
- Building 3D experiences within React applications
- Creating interactive product configurators or showcases
- Developing 3D portfolios, galleries, or storytelling experiences
- Building games or simulations in React
- Adding 3D elements to existing React projects
- When you need state management and React hooks with 3D graphics
- When working with React frameworks (Next.js, Gatsby, Remix)

**Key Benefits**:
- **Declarative**: Write 3D scenes like React components
- **React Integration**: Full access to hooks, context, state management
- **Reusability**: Create and share 3D component libraries
- **Performance**: Automatic render optimization and reconciliation
- **Ecosystem**: Works with Drei helpers, Zustand, Framer Motion, etc.
- **TypeScript Support**: Full type safety for Three.js objects

---

## Core Concepts

### 1. Canvas Component

The `` component sets up a Three.js scene, camera, renderer, and render loop.

```jsx
import { Canvas } from '@react-three/fiber'

function App() {
  return (
    
      {/* 3D content goes here */}
    
  )
}
```

**Canvas Props**:
- `camera` - Camera configuration (position, fov, near, far)
- `gl` - WebGL renderer settings
- `dpr` - Device pixel ratio (default: [1, 2])
- `shadows` - Enable shadow mapping (default: false)
- `frameloop` - "always" (default), "demand", or "never"
- `flat` - Disable color management for simpler colors
- `linear` - Use linear color space instead of sRGB

### 2. Declarative 3D Objects

Three.js objects are created using JSX with kebab-case props:

```jsx
// THREE.Mesh + THREE.BoxGeometry + THREE.MeshStandardMaterial

  
  

```

**Prop Mapping**:
- `position` → `object.position.set(x, y, z)`
- `rotation` → `object.rotation.set(x, y, z)`
- `scale` → `object.scale.set(x, y, z)`
- `args` → Constructor arguments for geometry/material
- `attach` → Attach to parent property (e.g., `attach="material"`)

**Shorthand Notation**:
```jsx
// Full notation

// Axis-specific (dash notation)

```

### 3. useFrame Hook

Execute code on every frame (animation loop):

```jsx
import { useFrame } from '@react-three/fiber'
import { useRef } from 'react'

function RotatingBox() {
  const meshRef = useRef()

  useFrame((state, delta) => {
    // Rotate mesh on every frame
    meshRef.current.rotation.x += delta
    meshRef.current.rotation.y += delta * 0.5

    // Access scene state
    const time = state.clock.elapsedTime
    meshRef.current.position.y = Math.sin(time) * 2
  })

  return (
    
      
      
    
  )
}
```

**useFrame Parameters**:
- `state` - Scene state (camera, scene, gl, clock, etc.)
- `delta` - Time since last frame (for frame-rate independence)
- `xrFrame` - XR frame data (for VR/AR)

**Important**: Never use `setState` inside `useFrame` - it causes unnecessary re-renders!

### 4. useThree Hook

Access scene state and methods:

```jsx
import { useThree } from '@react-three/fiber'

function CameraInfo() {
  const { camera, gl, scene, size, viewport } = useThree()

  // Selective subscription (only re-render on size change)
  const size = useThree((state) => state.size)

  // Get state non-reactively
  const get = useThree((state) => state.get)
  const freshState = get() // Latest state without triggering re-render

  return null
}
```

**Available State**:
- `camera` - Default camera
- `scene` - Three.js scene
- `gl` - WebGL renderer
- `size` - Canvas dimensions
- `viewport` - Viewport dimensions in 3D units
- `clock` - Three.js clock
- `pointer` - Normalized mouse coordinates
- `invalidate()` - Manually trigger render
- `setSize()` - Manually resize canvas

### 5. useLoader Hook

Load assets with automatic caching and Suspense integration:

```jsx
import { Suspense } from 'react'
import { useLoader } from '@react-three/fiber'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { TextureLoader } from 'three'

function Model() {
  const gltf = useLoader(GLTFLoader, '/model.glb')
  return 
}

function TexturedMesh() {
  const texture = useLoader(TextureLoader, '/texture.jpg')
  return (
    
      
      
    
  )
}

function App() {
  return (
    
      }>
        
        
      
    
  )
}
```

**Loading Multiple Assets**:
```jsx
const [texture1, texture2, texture3] = useLoader(TextureLoader, [
  '/tex1.jpg',
  '/tex2.jpg',
  '/tex3.jpg'
])
```

**Loader Extensions**:
```jsx
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'

useLoader(GLTFLoader, '/model.glb', (loader) => {
  const dracoLoader = new DRACOLoader()
  dracoLoader.setDecoderPath('/draco/')
  loader.setDRACOLoader(dracoLoader)
})
```

**Pre-loading**:
```jsx
// Pre-load assets before component mounts
useLoader.preload(GLTFLoader, '/model.glb')
```

---

## Common Patterns

### Pattern 1: Basic Scene Setup

```jsx
import { Canvas } from '@react-three/fiber'

function Scene() {
  return (
    <>
      {/* Lights */}
      
      

      {/* Objects */}
      
        
        
      
    
  )
}

function App() {
  return (
    
      
    
  )
}
```

### Pattern 2: Interactive Objects (Click, Hover)

```jsx
import { useState } from 'react'

function InteractiveBox() {
  const [hovered, setHovered] = useState(false)
  const [active, setActive] = useState(false)

  return (
     setActive(!active)}
      onPointerOver={() => setHovered(true)}
      onPointerOut={() => setHovered(false)}
    >
      
      
    
  )
}
```

### Pattern 3: Animated Component with useFrame

```jsx
import { useRef } from 'react'
import { useFrame } from '@react-three/fiber'

function AnimatedSphere() {
  const meshRef = useRef()

  useFrame((state, delta) => {
    // Rotate
    meshRef.current.rotation.y += delta

    // Oscillate position
    const time = state.clock.elapsedTime
    meshRef.current.position.y = Math.sin(time) * 2
  })

  return (
    
      
      
    
  )
}
```

### Pattern 4: Loading GLTF Models

```jsx
import { Suspense } from 'react'
import { useLoader } from '@react-three/fiber'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'

function Model({ url }) {
  const gltf = useLoader(GLTFLoader, url)

  return (
    
  )
}

function App() {
  return (
    
      }>
        
      
    
  )
}

function LoadingPlaceholder() {
  return (
    
      
      
    
  )
}
```

### Pattern 5: Multiple Lights

```jsx
function Lighting() {
  return (
    <>
      {/* Ambient light for base illumination */}
      

      {/* Directional light with shadows */}
      

      {/* Point light for accent */}
      

      {/* Spot light for focused illumination */}
      
    
  )
}
```

### Pattern 6: Instancing (Many Objects)

```jsx
import { useMemo, useRef } from 'react'
import * as THREE from 'three'
import { useFrame } from '@react-three/fiber'

function Particles({ count = 1000 }) {
  const meshRef = useRef()

  // Generate random positions
  const particles = useMemo(() => {
    const temp = []
    for (let i = 0; i  new THREE.Object3D(), [])

  useFrame(() => {
    particles.forEach((particle, i) => {
      let { t, factor, speed, x, y, z } = particle
      t = particle.t += speed / 2
      const a = Math.cos(t) + Math.sin(t * 1) / 10
      const b = Math.sin(t) + Math.cos(t * 2) / 10
      const s = Math.cos(t)

      dummy.position.set(
        x + Math.cos((t / 10) * factor) + (Math.sin(t * 1) * factor) / 10,
        y + Math.sin((t / 10) * factor) + (Math.cos(t * 2) * factor) / 10,
        z + Math.cos((t / 10) * factor) + (Math.sin(t * 3) * factor) / 10
      )
      dummy.scale.set(s, s, s)
      dummy.updateMatrix()
      meshRef.current.setMatrixAt(i, dummy.matrix)
    })
    meshRef.current.instanceMatrix.needsUpdate = true
  })

  return (
    
      
      
    
  )
}
```

### Pattern 7: Groups and Nesting

```jsx
function Robot() {
  return (
    
      {/* Body */}
      
        
        
      

      {/* Head */}
      
        
        
      

      {/* Arms */}
      
        
          
          
        
      

      
        
          
          
        
      
    
  )
}
```

---

## Integration with Drei Helpers

[Drei](https://github.com/pmndrs/drei) is the essential helper library for R3F, providing ready-to-use components:

### OrbitControls

```jsx
import { OrbitControls } from '@react-three/drei'

  
  

```

### Environment & Lighting

```jsx
import { Environment, ContactShadows } from '@react-three/drei'

  {/* HDRI environment map */}
  

  {/* Or custom */}
  

  {/* Soft contact shadows */}
  

  

```

### Text

```jsx
import { Text, Text3D } from '@react-three/drei'

// 2D Billboard text

  Hello World

// 3D extruded text

  3D Text
  

```

### useGLTF Hook (Drei)

```jsx
import { useGLTF } from '@react-three/drei'

function Model() {
  const { scene, materials, nodes } = useGLTF('/model.glb')

  return 
}

// Pre-load
useGLTF.preload('/model.glb')
```

### Center & Bounds

```jsx
import { Center, Bounds, useBounds } from '@react-three/drei'

// Auto-center objects

  

// Auto-fit camera to bounds

  

```

### HTML Overlay

```jsx
import { Html } from '@react-three/drei'

  
  

  
    
      This is a box
    
  

```

### Scroll Controls

```jsx
import { ScrollControls, Scroll, useScroll } from '@react-three/drei'
import { useFrame } from '@react-three/fiber'

function AnimatedScene() {
  const scroll = useScroll()
  const meshRef = useRef()

  useFrame(() => {
    const offset = scroll.offset // 0-1 normalized scroll position
    meshRef.current.position.y = offset * 10
  })

  return ...
}

  
    
      
    

    {/* HTML overlay */}
    
      
        Scrollable content
      
    
  

```

---

## Integration with Other Libraries

### With GSAP

```jsx
import { useRef, useEffect } from 'react'
import { useFrame } from '@react-three/fiber'
import gsap from 'gsap'

function AnimatedBox() {
  const meshRef = useRef()

  useEffect(() => {
    // GSAP timeline animation
    const tl = gsap.timeline({ repeat: -1, yoyo: true })

    tl.to(meshRef.current.position, {
      y: 2,
      duration: 1,
      ease: 'power2.inOut'
    })
    .to(meshRef.current.rotation, {
      y: Math.PI * 2,
      duration: 2,
      ease: 'none'
    }, 0)

    return () => tl.kill()
  }, [])

  return (
    
      
      
    
  )
}
```

### With Framer Motion

```jsx
import { motion } from 'framer-motion-3d'

function AnimatedSphere() {
  return (
    
      
      
    
  )
}
```

### With Zustand (State Management)

```jsx
import create from 'zustand'

const useStore = create((set) => ({
  color: 'orange',
  setColor: (color) => set({ color })
}))

function Box() {
  const color = useStore((state) => state.color)
  const setColor = useStore((state) => state.setColor)

  return (
     setColor('hotpink')}>
      
      
    
  )
}
```

---

## Performance Optimization

### 1. On-Demand Rendering

```jsx

  {/* Only renders when needed */}

// Manually trigger render
function MyComponent() {
  const invalidate = useThree((state) => state.invalidate)

  return (
     invalidate()}>
      
      
    
  )
}
```

### 2. Instancing

Use `` for rendering many identical objects:

```jsx
function Particles({ count = 10000 }) {
  const meshRef = useRef()

  useEffect(() => {
    const temp = new THREE.Object3D()

    for (let i = 0; i 
      
      
    
  )
}
```

### 3. Frustum Culling

Objects outside the camera view are automatically culled.

```jsx
// Disable for always-visible objects

  
  

```

### 4. LOD (Level of Detail)

```jsx
import { Detailed } from '@react-three/drei'

  {/* High detail - close to camera */}
  

  {/* Medium detail */}
  

  {/* Low detail - far from camera */}
  

```

### 5. Adaptive Performance

```jsx
import { AdaptiveDpr, AdaptiveEvents, PerformanceMonitor } from '@react-three/drei'

  {/* Reduce DPR when performance drops */}
  

  {/* Reduce raycast frequency */}
  

  {/* Monitor and respond to performance */}
   console.log('Performance improved')}
    onDecline={() => console.log('Performance degraded')}
  >
    
  

```

### 6. Selective Re-renders

Use `useThree` selectors to avoid unnecessary re-renders:

```jsx
// ❌ Re-renders on any state change
const state = useThree()

// ✅ Only re-renders when size changes
const size = useThree((state) => state.size)

// ✅ Only re-renders when camera changes
const camera = useThree((state) => state.camera)
```

---

## Common Pitfalls & Solutions

### ❌ Pitfall 1: setState in useFrame

```jsx
// ❌ BAD: Triggers React re-renders every frame
const [x, setX] = useState(0)
useFrame(() => setX((x) => x + 0.1))
return 
```

✅ **Solution**: Mutate refs directly

```jsx
// ✅ GOOD: Direct mutation, no re-renders
const meshRef = useRef()
useFrame((state, delta) => {
  meshRef.current.position.x += delta
})
return 
```

### ❌ Pitfall 2: Creating Objects in Render

```jsx
// ❌ BAD: Creates new Vector3 every render

```

✅ **Solution**: Use arrays or useMemo

```jsx
// ✅ GOOD: Use array notation

// Or useMemo for complex objects
const position = useMemo(() => new THREE.Vector3(1, 2, 3), [])

```

### ❌ Pitfall 3: Not Using useLoader Cache

```jsx
// ❌ BAD: Loads texture every render
function Component() {
  const [texture, setTexture] = useState()
  useEffect(() => {
    new TextureLoader().load('/texture.jpg', setTexture)
  }, [])
  return texture ?  : null
}
```

✅ **Solution**: Use useLoader (automatic caching)

```jsx
// ✅ GOOD: Cached and reused
function Component() {
  const texture = useLoader(TextureLoader, '/texture.jpg')
  return 
}
```

### ❌ Pitfall 4: Conditional Mounting (Expensive)

```jsx
// ❌ BAD: Unmounts and remounts (expensive)
{stage === 1 && }
{stage === 2 && }
{stage === 3 && }
```

✅ **Solution**: Use visibility prop

```jsx
// ✅ GOOD: Components stay mounted, just hidden

function Stage1({ visible, ...props }) {
  return ...
}
```

### ❌ Pitfall 5: useThree Outside Canvas

```jsx
// ❌ BAD: Crashes - useThree must be inside Canvas
function App() {
  const { size } = useThree()
  return ...
}
```

✅ **Solution**: Use hooks inside Canvas children

```jsx
// ✅ GOOD: useThree inside Canvas child
function CameraInfo() {
  const { size } = useThree()
  return null
}

function App() {
  return (
    
      
    
  )
}
```

### ❌ Pitfall 6: Not Disposing Resources

```jsx
// ❌ BAD: Memory leak - textures not disposed
const texture = useLoader(TextureLoader, '/texture.jpg')
```

✅ **Solution**: R3F handles disposal automatically, but be careful with manual Three.js objects

```jsx
// ✅ GOOD: Manual cleanup when needed
useEffect(() => {
  const geometry = new THREE.SphereGeometry(1)
  const material = new THREE.MeshBasicMaterial()

  return () => {
    geometry.dispose()
    material.dispose()
  }
}, [])
```

---

## Best Practices

### 1. Component Composition

Break scenes into reusable components:

```jsx
function Lights() {
  return (
    <>
      
      
    
  )
}

function Scene() {
  return (
    <>
      
      
      
      
    
  )
}

  

```

### 2. Suspend Heavy Assets

Always wrap async operations in Suspense:

```jsx

  }>
    
    
  

```

### 3. Use TypeScript

```typescript
import { ThreeElements } from '@react-three/fiber'

function Box(props: ThreeElements['mesh']) {
  return (
    
      
      
    
  )
}
```

### 4. Organize by Feature

```
src/
  components/
    3d/
      Scene.tsx
      Lights.tsx
      Camera.tsx
    models/
      Robot.tsx
      Character.tsx
    effects/
      PostProcessing.tsx
```

### 5. Test with React DevTools Profiler

Monitor re-renders and optimize components causing performance issues.

---

## Resources

### References
- `references/api_reference.md` - Complete R3F & Drei API documentation
- `references/hooks_guide.md` - Det

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [freshtechbro](https://github.com/freshtechbro)
- **Source:** [freshtechbro/claudedesignskills](https://github.com/freshtechbro/claudedesignskills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-freshtechbro-claudedesignskills-react-three-fiber
- Seller: https://agentstack.voostack.com/s/freshtechbro
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
