Skip to content
r3f-fundamentals logo

React Three Fiber Fundamentals

r3f-fundamentals

React Three Fiber core setup, Canvas configuration, scene hierarchy, camera systems, lighting, render loop, and React integration patterns. Use when setting up a new R3F project, configuring the Canvas component, managing scene structure, or understanding the declarative Three.js-in-React paradig...

NeverSight/skills_feed0installs215starsDesign

SKILL.md

Full skill instructions

React Three Fiber Fundamentals

Declarative Three.js via React components. R3F maps Three.js objects to JSX elements with automatic disposal, reactive updates, and React lifecycle integration.

Quick Start

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

function App() {
  return (
    <Canvas>
      <ambientLight intensity={0.5} />
      <pointLight position={[10, 10, 10]} />
      <mesh>
        <boxGeometry args={[1, 1, 1]} />
        <meshStandardMaterial color="hotpink" />
      </​mesh>
    </​Canvas>
  );
}

Core Principle: Declarative Scene Graph

R3F converts Three.js imperative API to React's declarative model:

Three.js (Imperative)R3F (Declarative)
new THREE.Mesh()<mesh>
mesh.position.set(1, 2, 3)<mesh position={[1, 2, 3]}>
scene.add(mesh)JSX nesting handles hierarchy
mesh.geometry.dispose()Automatic on unmount

Canvas Configuration

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

<Canvas
  // Renderer settings
  gl={{ antialias: true, alpha: false, powerPreference: 'high-performance' }}
  dpr={[1, 2]}                    // Device pixel ratio range
  shadows                          // Enable shadow maps
  
  // Camera (default: PerspectiveCamera)
  camera={{ 
    fov: 75, 
    near: 0.1, 
    far: 1000, 
    position: [0, 0, 5] 
  }}
  
  // Or use orthographic
  orthographic
  camera={{ zoom: 50, position: [0, 0, 100] }}
  
  // Performance
  frameloop="demand"              // 'always' | 'demand' | 'never'
  performance={{ min: 0.5 }}      // Adaptive performance
  
  // Events
  onCreated={({ gl, scene, camera }) => {
    // Access Three.js objects after mount
  }}
  
  // Sizing
  style={{ width: '100vw', height: '100vh' }}
/>

Frameloop Modes

ModeWhen to Use
alwaysContinuous animation (games, simulations)
demandStatic scenes, only re-render on state change
neverManual control via invalidate()
// Demand mode with manual invalidation
import { useThree } from '@react-three/​fiber';

function Controls() {
  const invalidate = useThree(state => state.invalidate);
  
  const handleDrag = () => {
    // Update state...
    invalidate(); // Request re-render
  };
}

Scene Hierarchy

JSX nesting = Three.js parent-child relationships:

<group position={[0, 2, 0]} rotation={[0, Math.PI / 4, 0]}>
  {/​* Children inherit parent transforms */​}
  <mesh position={[1, 0, 0]}>
    <sphereGeometry args={[0.5, 32, 32]} />
    <meshStandardMaterial color="blue" />
  </​mesh>
  
  <mesh position={[-1, 0, 0]}>
    <boxGeometry args={[0.8, 0.8, 0.8]} />
    <meshStandardMaterial color="red" />
  </​mesh>
</​group>

Common Container Components

// Group: Transform container (no rendering)
<group position={[0, 0, 0]} />

// Object3D: Base class, rarely used directly
<object3D />

// Scene: Usually implicit (Canvas creates one)
<scene />

Camera Systems

Default Perspective Camera

<Canvas camera={{ 
  fov: 75,              // Field of view (degrees)
  aspect: width/​height, // Auto-calculated
  near: 0.1,            // Near clipping plane
  far: 1000,            // Far clipping plane
  position: [0, 5, 10]
}} />

Custom Camera Component

import { PerspectiveCamera } from '@react-three/​drei';

function Scene() {
  return (
    <>
      <PerspectiveCamera 
        makeDefault           // Set as active camera
        fov={60} 
        position={[0, 2, 8]} 
      />
      {/​* Scene contents */​}
    </>
  );
}

Camera Access

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

function CameraController() {
  const { camera } = useThree();
  
  useEffect(() => {
    camera.lookAt(0, 0, 0);
  }, [camera]);
  
  return null;
}

Lighting

Light Types

// Ambient: Uniform, directionless
<ambientLight intensity={0.4} color="#ffffff" />

// Directional: Sun-like, parallel rays
<directionalLight 
  position={[5, 10, 5]} 
  intensity={1} 
  castShadow 
/>

// Point: Radiates from position
<pointLight 
  position={[0, 5, 0]} 
  intensity={1} 
  distance={20}        // Range (0 = infinite)
  decay={2}            // Physical falloff
/>

// Spot: Cone-shaped
<spotLight 
  position={[0, 10, 0]} 
  angle={Math.PI / 6}  // Cone angle
  penumbra={0.5}       // Edge softness
  castShadow 
/>

// Hemisphere: Sky/​ground gradient
<hemisphereLight 
  skyColor="#87ceeb" 
  groundColor="#362907" 
  intensity={0.6} 
/>

Shadow Setup

<Canvas shadows>
  <directionalLight
    castShadow
    position={[10, 10, 10]}
    shadow-mapSize={[2048, 2048]}
    shadow-camera-far={50}
    shadow-camera-left={-10}
    shadow-camera-right={10}
    shadow-camera-top={10}
    shadow-camera-bottom={-10}
  />
  
  <mesh castShadow>
    <boxGeometry />
    <meshStandardMaterial />
  </​mesh>
  
  <mesh receiveShadow rotation={[-Math.PI / 2, 0, 0]} position={[0, -1, 0]}>
    <planeGeometry args={[20, 20]} />
    <meshStandardMaterial />
  </​mesh>
</​Canvas>

Render Loop (useFrame)

useFrame runs every frame (60fps target). This is where animation happens.

import { useFrame, useThree } from '@react-three/​fiber';
import { useRef } from 'react';

function RotatingBox() {
  const meshRef = useRef<THREE.Mesh>(null!);
  
  useFrame((state, delta) => {
    // state: R3F state (camera, scene, clock, etc.)
    // delta: Time since last frame (seconds)
    
    meshRef.current.rotation.x += delta;
    meshRef.current.rotation.y += delta * 0.5;
  });
  
  return (
    <mesh ref={meshRef}>
      <boxGeometry />
      <meshNormalMaterial />
    </​mesh>
  );
}

useFrame State Object

useFrame((state) => {
  state.clock        // THREE.Clock
  state.clock.elapsedTime  // Total time (seconds)
  state.camera       // Active camera
  state.scene        // Scene object
  state.gl           // WebGLRenderer
  state.size         // { width, height }
  state.viewport     // { width, height, factor, distance }
  state.mouse        // Normalized mouse position [-1, 1]
  state.raycaster    // THREE.Raycaster
});

Render Priority

// Lower priority runs first, higher runs later
// Default is 0

useFrame(() => {
  // Update physics
}, -1);  // Runs before default

useFrame(() => {
  // Update visuals
}, 0);   // Default

useFrame(() => {
  // Post-processing / camera
}, 1);   // Runs after default

Accessing Three.js Objects

useThree Hook

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

function SceneInfo() {
  const { 
    gl,           // WebGLRenderer
    scene,        // THREE.Scene
    camera,       // Active camera
    size,         // Canvas dimensions
    viewport,     // Viewport in Three.js units
    clock,        // THREE.Clock
    set,          // Update state
    get,          // Get current state
    invalidate,   // Request re-render (demand mode)
    advance,      // Advance one frame (never mode)
  } = useThree();
  
  return null;
}

Refs for Direct Access

import { useRef } from 'react';
import * as THREE from 'three';

function DirectAccess() {
  const meshRef = useRef<THREE.Mesh>(null!);
  const materialRef = useRef<THREE.MeshStandardMaterial>(null!);
  
  useEffect(() => {
    // Direct Three.js API access
    meshRef.current.geometry.computeBoundingBox();
    materialRef.current.needsUpdate = true;
  }, []);
  
  return (
    <mesh ref={meshRef}>
      <boxGeometry />
      <meshStandardMaterial ref={materialRef} />
    </​mesh>
  );
}

Events

R3F provides pointer events on meshes:

<mesh
  onClick={(e) => console.log('click', e.point)}
  onContextMenu={(e) => console.log('right click')}
  onDoubleClick={(e) => console.log('double click')}
  onPointerOver={(e) => console.log('hover')}
  onPointerOut={(e) => console.log('unhover')}
  onPointerDown={(e) => console.log('down')}
  onPointerUp={(e) => console.log('up')}
  onPointerMove={(e) => console.log('move')}
>
  <boxGeometry />
  <meshStandardMaterial />
</​mesh>

Event Object

onClick={(e) => {
  e.stopPropagation();    // Stop event bubbling
  e.point                 // THREE.Vector3 intersection point
  e.distance              // Distance from camera
  e.object                // Intersected object
  e.face                  // Intersected face
  e.faceIndex             // Face index
  e.uv                    // UV coordinates
  e.camera                // Camera used for raycasting
  e.delta                 // Distance from last event
}}

Suspense & Loading

R3F integrates with React Suspense for async loading:

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

function Model() {
  const gltf = useLoader(GLTFLoader, '/​model.glb');
  return <primitive object={gltf.scene} />;
}

function App() {
  return (
    <Canvas>
      <Suspense fallback={<LoadingSpinner />}>
        <Model />
      </​Suspense>
    </​Canvas>
  );
}

function LoadingSpinner() {
  const meshRef = useRef<THREE.Mesh>(null!);
  useFrame((_, delta) => {
    meshRef.current.rotation.z += delta * 2;
  });
  
  return (
    <mesh ref={meshRef}>
      <torusGeometry args={[1, 0.2, 16, 32]} />
      <meshBasicMaterial color="white" wireframe />
    </​mesh>
  );
}

Dependencies

{
  "dependencies": {
    "@react-three/​fiber": "^8.15.0",
    "three": "^0.160.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@types/​three": "^0.160.0"
  }
}

File Structure

r3f-fundamentals/
├── SKILL.md
├── references/
│   ├── canvas-props.md       # Complete Canvas prop reference
│   ├── hooks-api.md          # useThree, useFrame, useLoader
│   └── event-system.md       # Event handling deep-dive
└── scripts/
    ├── templates/
    │   ├── basic-scene.tsx   # Minimal starter
    │   ├── lit-scene.tsx     # With proper lighting
    │   └── interactive.tsx   # With events and animation
    └── utils/
        └── canvas-config.ts  # Preset configurations

Reference

  • references/​canvas-props.md — Complete Canvas configuration options
  • references/​hooks-api.md — All R3F hooks with examples
  • references/​event-system.md — Pointer events and raycasting

More Design skills

brandkit logo
Design

brandkit

Premium brand-kit image generation for high-end identity systems, logo concepts, and visual-world presentations.

92.7K 345.8K
View

Premium website design-to-code skill that generates visual references first, then builds faithful frontend implementations.

92.7K 341.7K
View

Premium frontend image direction for landing pages and marketing sites, generating one distinct horizontal image per section.

92.7K 337.8K
View

Premium mobile app screen concepts and flows with clean hierarchy, strong imagery, and distinctive art direction.

92.7K 331.8K
View

Emil Kowalski's philosophy on UI polish, component design, and the invisible details that make software feel good.

43.5K 323.4K
View

Frontend design and iteration for production-grade interfaces, from concept to ship.

76.8K 313.3K
View

Reverse-engineer design tokens from public websites into starter token files for your project.

232 129.9K
View
Animate logo
Design

Animate

Builds an animation from scratch by making the decisions in the order that determines whether it feels right.

43.5K 117.9K
View

Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.

179.7K 114.2K
View

Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.

179.7K 100.1K
View

Toolkit for styling artifacts with a theme. These artifacts can be slides, docs, reportings, HTML landing pages, etc. There are 10 pre-set themes with colors/fonts that you can apply to any artifact that has been creating, or can generate a new theme on-the-fly.

179.7K 89.5K
View

Design AI tools

Maze Guru logo
Images

Maze Guru

Chat-to-edit AI studio for product photos, portraits, branding, and video.

Freemium
View
R
Design

RoomGPT

Generate your dream room in seconds.

Paid
View

Redesign your interior in seconds using AI

Paid
View
Reroom logo
Design

Reroom

Transform rooms with AI-generated designs.

Freemium
View
Jife logo
Design

Jife

Transform Your Space with AI-Powered Japanese Interior Designs by Jife

Paid
View
Visily logo
Design

Visily

AI-powered wireframes for non-designers.

Free
View

Revolutionize Property Viewing with AI-Generated Floor Plans and Virtual Tours

Paid
View
Archsynth logo
Design

Archsynth

Transform sketches into stunning architectural renders with AI.

Paid
View