iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

react-three-fiber

React Three Fiber (R3F) is React for Three.js: scene graph as JSX, hooks for animation and state, Suspense for loaders, declarative components for everything. Combined with @react-three/drei helpers it’s the most productive way to ship 3D in a React app.

Canvas, useFrame, drei, loaders

EXAMPLE
// 1) Install
// npm install three @react-three/fiber @react-three/drei @react-three/postprocessing

import { Canvas, useFrame, useThree } from '@react-three/fiber';
import { OrbitControls, Environment, useGLTF, ContactShadows, Html, Text, Stage } from '@react-three/drei';
import { Suspense, useRef, useState } from 'react';
import * as THREE from 'three';

// 2) Minimal scene
function Box(props) {
    const ref = useRef();
    const [hovered, setHover] = useState(false);
    const [active, setActive] = useState(false);

    useFrame((state, dt) => {
        ref.current.rotation.x += dt;
        ref.current.rotation.y += dt * 0.5;
    });

    return (
        <mesh
            {...props}
            ref={ref}
            scale={active ? 1.5 : 1}
            onClick={() => setActive((s) => !s)}
            onPointerOver={() => setHover(true)}
            onPointerOut={() => setHover(false)}
        >
            <boxGeometry args={[1, 1, 1]} />
            <meshStandardMaterial color={hovered ? 'orange' : '#4f46e5'} />
        </mesh>
    );
}

// 3) Canvas — the root that mounts Three.js inside React
export function App() {
    return (
        <Canvas
            camera={{ position: [3, 3, 5], fov: 50 }}
            shadows
            gl={{ antialias: true, outputColorSpace: THREE.SRGBColorSpace }}
        >
            <ambientLight intensity={0.3} />
            <directionalLight position={[5, 5, 5]} castShadow intensity={1.2} />
            <Box position={[-1, 0, 0]} />
            <Box position={[1, 0, 0]} />
            <OrbitControls />
        </Canvas>
    );
}

// 4) Loading a glTF — Suspense + drei
function Model({ url }) {
    const { scene } = useGLTF(url);
    return <primitive object={scene} />;
}

<Canvas>
    <Suspense fallback={<Html>Loading…</Html>}>
        <Model url="/helmet.glb" />
        <Environment preset="sunset" />
        <ContactShadows position={[0, -1, 0]} opacity={0.5} scale={10} blur={2.5} far={4} />
    </Suspense>
</Canvas>

// useGLTF preloads — call useGLTF.preload(url) outside of render.

// 5) Drei staples
// • OrbitControls / TrackballControls / FlyControls
// • Environment (HDRI lighting)
// • Sky / Stars / Cloud — natural backdrops
// • Stage — pre-configured lighting + ground
// • Float / Center / Bounds — auto-fit and gentle motion
// • Html — render React UI inside the 3D scene
// • Text — 3D text via troika
// • PerspectiveCamera / OrthographicCamera — camera primitives
// • Outlines / Edges — visual effects
// • Sparkles / Trail / Lightformer — fancy helpers

// 6) useFrame — runs every frame; sync state, animate, update
function Spinner() {
    const ref = useRef();
    useFrame((state, dt) => {
        ref.current.rotation.y += dt * 0.5;
        ref.current.position.y = Math.sin(state.clock.elapsedTime) * 0.3;
    });
    return <mesh ref={ref}><torusGeometry args={[0.6, 0.2, 16, 100]} /><meshStandardMaterial color="violet" /></mesh>;
}

// 7) Pointer events
// onClick, onContextMenu, onDoubleClick, onPointerUp/Down/Over/Out/Move/Cancel, onWheel
// Events are raycasted automatically. Use 'stopPropagation' to prevent bubbling.

// 8) useThree — access the underlying Three.js renderer/scene/camera/etc.
function Logger() {
    const { camera, gl, scene, viewport, size } = useThree();
    console.log('camera position', camera.position);
    return null;
}

// 9) Resize-friendly + viewport scaling
<Canvas dpr={[1, 2]}                       /* device pixel ratio clamp */
              resize={{ scroll: true, debounce: { scroll: 50, resize: 0 } }}>
    {/* … */}
</Canvas>

// 10) Performance modes
<Canvas frameloop="demand">                {/* renders only when invalidated */}
    <RotatingBox onUpdate={() => invalidate()} />
</Canvas>

// Or 'frameloop="never"' and call invalidate() manually for snapshot-style scenes.

// 11) Post-processing (bloom, DOF, etc.)
import { EffectComposer, Bloom, Vignette } from '@react-three/postprocessing';
<Canvas>
    {/* scene */}
    <EffectComposer>
        <Bloom intensity={0.6} luminanceThreshold={0.7} />
        <Vignette eskil={false} offset={0.1} darkness={1.1} />
    </EffectComposer>
</Canvas>

// 12) Physics (Rapier / Cannon)
import { Physics, RigidBody, CuboidCollider } from '@react-three/rapier';
<Canvas>
    <Physics gravity={[0, -9.8, 0]}>
        <RigidBody>
            <Box position={[0, 5, 0]} />
        </RigidBody>
        <RigidBody type="fixed">
            <CuboidCollider args={[10, 0.1, 10]} />
            <mesh position={[0, -0.1, 0]} receiveShadow>
                <boxGeometry args={[20, 0.2, 20]} />
                <meshStandardMaterial color="#88aa66" />
            </mesh>
        </RigidBody>
    </Physics>
</Canvas>

// 13) Custom shaders
import { shaderMaterial } from '@react-three/drei';
import { extend } from '@react-three/fiber';

const MyMaterial = shaderMaterial(
    { uTime: 0, uColor: new THREE.Color('orange') },
    /* vertex */ `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,
    /* frag */   `uniform float uTime; uniform vec3 uColor; varying vec2 vUv;
                  void main() { gl_FragColor = vec4(uColor * (0.5 + 0.5 * sin(uTime + vUv.x * 5.0)), 1.0); }`,
);
extend({ MyMaterial });

function WavyPlane() {
    const ref = useRef();
    useFrame(({ clock }) => { ref.current.uTime = clock.elapsedTime; });
    return <mesh><planeGeometry args={[2, 2, 64, 64]} /><myMaterial ref={ref} /></mesh>;
}

// 14) Common bugs
// • Forgot Canvas → throwing 'R3F: Hooks can only be used within the Canvas component'
// • Setting mesh ref but no children → empty render; check JSX nesting
// • useGLTF without preload + Suspense → tearing / blank flashes
// • shadows but no castShadow/receiveShadow set on meshes
// • Heavy useFrame closures cause GC pauses → memoise references with useRef + useCallback
// • Multiple <Canvas> on a page sharing state — keep one per scene
// • Pointer events firing for hidden meshes — use raycast={null} or layer filtering
// • HMR on non-React Three code — refresh required

Why it matters

React Three Fiber turns 3D into idiomatic React: components for objects, useFrame for per-frame logic, Suspense + drei loaders for models and HDRIs, optional Rapier physics, and post-processing as JSX. frameloop="demand" + render-on-change keeps cost low for static or sporadic scenes.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
import { Canvas } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
<Canvas><OrbitControls /><mesh /></Canvas>
Try it Yourself »

Discussion

Loading…