react-three-fiber

React Three Fiber (R3F) is a React renderer for Three.js. Write declarative, component-based 3D scenes using JSX.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "react-three-fiber" with this command: npx skills add anthemflynn/ccmp/anthemflynn-ccmp-react-three-fiber

React Three Fiber

React Three Fiber (R3F) is a React renderer for Three.js. Write declarative, component-based 3D scenes using JSX.

Library Versions (2026)

  • React Three Fiber: v9.5+

  • @react-three/drei: v9.116+

  • @react-three/rapier: v2+

  • @react-three/postprocessing: v3+

  • React: 19+ (concurrent features supported)

Decision Frameworks

When to Use R3F vs Vanilla Three.js

Is your app already React-based? → Yes: Use R3F (natural integration) → No: Consider vanilla Three.js

Do you need React state management? → Yes: Use R3F (seamless state integration) → No: Either works

Is the 3D scene the entire app? → Yes: Either works (R3F has slight overhead) → No, mixed with React UI: Use R3F

Performance-critical with millions of objects? → Consider vanilla Three.js for maximum control → R3F is fine for 99% of use cases

When to Use Which State Management

Local component state (single mesh color, hover)? → useState / useRef

Shared between few components (selected object)? → React Context or prop drilling

Global game/app state (score, inventory, settings)? → Zustand (recommended for R3F)

Complex state with actions/reducers? → Zustand with slices or Redux Toolkit

Need state persistence? → Zustand with persist middleware

When to Use Which Drei Component

Need camera controls? ├─ Orbit around object → OrbitControls ├─ First-person → PointerLockControls ├─ Map/top-down → MapControls └─ Smooth transitions → CameraControls

Need environment lighting? ├─ Quick preset → <Environment preset="sunset" /> ├─ Custom HDR → <Environment files="/env.hdr" /> └─ Performance-sensitive → <Environment blur={0.5} />

Need text? ├─ 3D text in scene → <Text3D /> ├─ 2D text billboards → <Text /> └─ HTML overlay → <Html />

Need loading? ├─ GLTF models → useGLTF ├─ Textures → useTexture ├─ Progress UI → useProgress └─ Preloading → <Preload all />

Core Setup

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

function App() { return ( <Canvas camera={{ position: [0, 0, 5], fov: 75 }} gl={{ antialias: true }} shadows > <ambientLight intensity={0.5} /> <directionalLight position={[10, 10, 5]} castShadow /> <mesh> <boxGeometry args={[1, 1, 1]} /> <meshStandardMaterial color="orange" /> </mesh> </Canvas> ) }

Canvas Props

<Canvas camera={{ position, fov, near, far }} // Camera config gl={{ antialias, alpha, powerPreference }} // WebGL context shadows // Enable shadow maps dpr={[1, 2]} // Device pixel ratio range frameloop="demand" // "always" | "demand" | "never" style={{ width: '100%', height: '100vh' }} onCreated={({ gl, scene, camera }) => {}} // Access internals />

Essential Hooks

useFrame - Animation Loop

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

function SpinningBox() { const meshRef = useRef()

useFrame((state, delta) => { // state: { clock, camera, scene, gl, pointer, ... } meshRef.current.rotation.y += delta meshRef.current.position.y = Math.sin(state.clock.elapsedTime) })

return ( <mesh ref={meshRef}> <boxGeometry /> <meshStandardMaterial /> </mesh> ) }

useThree - Access Internals

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

function CameraLogger() { const { camera, gl, scene, size, viewport, pointer } = useThree() // viewport: { width, height } in Three.js units // size: { width, height } in pixels // pointer: normalized mouse position (-1 to 1) return null }

useLoader - Asset Loading

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

function Model() { const gltf = useLoader(GLTFLoader, '/model.glb') const texture = useLoader(TextureLoader, '/texture.jpg')

return <primitive object={gltf.scene} /> }

JSX to Three.js Mapping

// Three.js class → camelCase JSX element // Constructor args → args prop (array) // Properties → props

// THREE.Mesh <mesh position={[0, 1, 0]} rotation={[0, Math.PI, 0]} scale={1.5}> {/* THREE.BoxGeometry(1, 2, 1) /} <boxGeometry args={[1, 2, 1]} /> {/ THREE.MeshStandardMaterial({ color: 'red' }) */} <meshStandardMaterial color="red" roughness={0.5} /> </mesh>

// Nested properties use dash notation <directionalLight position={[5, 5, 5]} castShadow shadow-mapSize={[2048, 2048]} shadow-camera-far={50} />

// Attach to parent properties <mesh> <boxGeometry /> <meshStandardMaterial> <texture attach="map" image={img} /> </meshStandardMaterial> </mesh>

Drei - Essential Helpers

Drei provides production-ready abstractions. Install: @react-three/drei

import { OrbitControls, PerspectiveCamera, Environment, useGLTF, useTexture, Text, Html, Float, Stage, ContactShadows, Sky, Stars, } from '@react-three/drei'

Common Drei Components

// Camera controls <OrbitControls enableDamping dampingFactor={0.05} />

// Environment lighting (HDR) <Environment preset="sunset" background /> // Presets: apartment, city, dawn, forest, lobby, night, park, studio, sunset, warehouse

// Load GLTF with draco support const { scene, nodes, materials } = useGLTF('/model.glb') useGLTF.preload('/model.glb')

// Load textures const [colorMap, normalMap] = useTexture(['/color.jpg', '/normal.jpg'])

// 3D Text <Text fontSize={1} color="white" anchorX="center" anchorY="middle"> Hello World </Text>

// HTML overlay in 3D space <Html position={[0, 2, 0]} center transform occlude> <div className="label">Click me</div> </Html>

// Floating animation <Float speed={2} rotationIntensity={0.5} floatIntensity={1}> <mesh>...</mesh> </Float>

// Quick studio lighting <Stage environment="city" intensity={0.5}> <Model /> </Stage>

// Soft shadows on ground <ContactShadows position={[0, -0.5, 0]} opacity={0.5} blur={2} />

Event Handling

<mesh onClick={(e) => { e.stopPropagation() console.log('clicked', e.point, e.face) }} onPointerOver={(e) => setHovered(true)} onPointerOut={(e) => setHovered(false)} onPointerMove={(e) => console.log(e.point)} onPointerDown={(e) => {}} onPointerUp={(e) => {}} onDoubleClick={(e) => {}} onContextMenu={(e) => {}}

Event object includes: point , face , faceIndex , distance , object , eventObject , camera , ray .

State Management

With Zustand (Recommended)

import { create } from 'zustand'

const useStore = create((set) => ({ score: 0, gameState: 'idle', addScore: (points) => set((state) => ({ score: state.score + points })), startGame: () => set({ gameState: 'playing' }), }))

function ScoreDisplay() { const score = useStore((state) => state.score) return <Text>{score}</Text> }

function GameLogic() { const addScore = useStore((state) => state.addScore) useFrame(() => { // Game logic that calls addScore }) return null }

With React Context

const GameContext = createContext()

function GameProvider({ children }) { const [state, dispatch] = useReducer(reducer, initialState) return ( <GameContext.Provider value={{ state, dispatch }}> {children} </GameContext.Provider> ) }

// Wrap Canvas content <Canvas> <GameProvider> <Scene /> </GameProvider> </Canvas>

Performance Patterns

Instancing

import { Instances, Instance } from '@react-three/drei'

function Boxes({ count = 1000 }) { return ( <Instances limit={count}> <boxGeometry /> <meshStandardMaterial /> {Array.from({ length: count }, (_, i) => ( <Instance key={i} position={[Math.random() * 100, Math.random() * 100, Math.random() * 100]} color={hsl(${Math.random() * 360}, 50%, 50%)} /> ))} </Instances> ) }

Selective Rendering

// Only re-render when needed <Canvas frameloop="demand"> {/* Call invalidate() to trigger render */} </Canvas>

function Controls() { const { invalidate } = useThree() return <OrbitControls onChange={() => invalidate()} /> }

Memoization

// Memoize expensive components const ExpensiveModel = memo(function ExpensiveModel({ url }) { const gltf = useGLTF(url) return <primitive object={gltf.scene.clone()} /> })

// Memoize materials/geometries outside components const geometry = new THREE.BoxGeometry(1, 1, 1) const material = new THREE.MeshStandardMaterial({ color: 'red' })

function Box() { return ( <mesh geometry={geometry} material={material} /> ) }

Adaptive Performance

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

<Canvas> <PerformanceMonitor onDecline={() => setQuality('low')} onIncline={() => setQuality('high')}

&#x3C;AdaptiveDpr pixelated />
&#x3C;AdaptiveEvents />
&#x3C;Scene quality={quality} />

</PerformanceMonitor> </Canvas>

Related Skills

When you need... Use skill

Vanilla Three.js (no React) → threejs

Optimize assets before loading → asset-pipeline-3d

Debug visual/performance issues → graphics-troubleshooting

Reference Files

  • references/drei.md - Complete Drei component reference

  • references/physics.md - @react-three/rapier integration

  • references/postprocessing.md - @react-three/postprocessing effects

  • references/state.md - Zustand patterns for R3F

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

asset-pipeline-3d

No summary provided by upstream source.

Repository SourceNeeds Review
General

openclaw-maintain

No summary provided by upstream source.

Repository SourceNeeds Review
General

claude-context-manager

No summary provided by upstream source.

Repository SourceNeeds Review