Skip to content
Back to skills

React Three Fiber

ASecurity

Builds 3D scenes in React with React Three Fiber (R3F), a React renderer for Three.js where meshes, lights and cameras are JSX components. Use when a user asks to add a 3D scene to a React or Next.js site, build a product configurator, load a GLTF model, animate with useFrame, handle click and hover on 3D objects, or speed up a slow R3F scene.

  • 142 stars
  • 0 votes
  • 0 copies
  • 6 views
  • Added September 6, 2026
developmenttypescriptgobashreactnextjsgitapiperformance

Works with

  • terminal
  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 2 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add TerminalSkills/skills --skill react-three-fiber --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of React Three Fiber?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for React Three Fiber
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/terminalskills-react-three-fiber/badge)](https://www.skillsdirectory.com/skills/terminalskills-react-three-fiber)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: react-three-fiber
description: >-
  Builds 3D scenes in React with React Three Fiber (R3F), a React renderer for Three.js where meshes, lights and cameras are JSX components. Use when a user asks to add a 3D scene to a React or Next.js site, build a product configurator, load a GLTF model, animate with useFrame, handle click and hover on 3D objects, or speed up a slow R3F scene.
license: Apache-2.0
compatibility: 'R3F 9.x needs React 19 and three 0.156 or newer; drei 10.x pairs with R3F 9. React 18 projects use R3F 8 and drei 9.'
metadata:
  author: terminal-skills
  version: "1.1.0"
  category: development
  tags:
    - 3d
    - webgl
    - react
    - threejs
    - r3f
  repository: https://github.com/pmndrs/react-three-fiber
---

# React Three Fiber — Declarative Three.js for React

## Overview

React Three Fiber (R3F) is a React renderer for Three.js. You write scenes as JSX (`<mesh>`, `<boxGeometry>`, `<meshStandardMaterial>`), and props map straight onto Three.js object properties. Hooks, state, Suspense and context work as in any React app, and everything in the Three.js ecosystem stays available. `@react-three/drei` adds ready-made helpers (controls, loaders, environment maps, text, HTML overlays).

Current versions: `@react-three/fiber` 9.8, `@react-three/drei` 10.7, `three` 0.186. R3F 9 requires React 19; with React 18 install `@react-three/fiber@8` and `@react-three/drei@9`.

## Instructions

### Install

```bash
npm install three @react-three/fiber @react-three/drei
npm install -D @types/three                 # TypeScript
npm install @react-three/postprocessing     # optional: bloom, depth of field, etc.
```

In Next.js, components that render a `<Canvas>` must be client components (`'use client'`). If a Three.js add-on ships untranspiled code, add `transpilePackages: ['three']` to `next.config.js`.

### Canvas and scene

```tsx
'use client'
import { Canvas } from '@react-three/fiber'
import { OrbitControls, Environment } from '@react-three/drei'

export default function Scene() {
  return (
    <Canvas camera={{ position: [0, 2, 5], fov: 50 }} dpr={[1, 2]} shadows>
      <ambientLight intensity={0.4} />
      <directionalLight position={[5, 5, 5]} castShadow />
      <mesh position={[0, 1, 0]} castShadow>
        <boxGeometry args={[1, 1, 1]} />
        <meshStandardMaterial color="orange" />
      </mesh>
      <mesh rotation={[-Math.PI / 2, 0, 0]} receiveShadow>
        <planeGeometry args={[10, 10]} />
        <meshStandardMaterial color="#444" />
      </mesh>
      <OrbitControls />
      <Environment preset="city" />
    </Canvas>
  )
}
```

`Canvas` fills its parent, so give the parent a height. Defaults: `dpr` `[1, 2]`, sRGB output and ACES filmic tone mapping, a translucent WebGL renderer with antialiasing. Pass `fallback={<p>WebGL is not available</p>}` for devices without WebGL.

### Animation with useFrame

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

function SpinningBox() {
  const meshRef = useRef<THREE.Mesh>(null)
  useFrame((state, delta) => {
    if (!meshRef.current) return
    meshRef.current.rotation.y += delta                       // frame-rate independent
    meshRef.current.position.x = THREE.MathUtils.lerp(
      meshRef.current.position.x, state.pointer.x * 2, 0.1,   // pointer is -1..1; state.mouse is deprecated
    )
  })
  return (
    <mesh ref={meshRef}>
      <boxGeometry />
      <meshStandardMaterial color="hotpink" />
    </mesh>
  )
}
```

Mutate refs inside `useFrame`; never call `setState` there. `useThree()` returns the renderer, scene, camera, viewport and size.

### Loading models

```tsx
import { Suspense, useEffect } from 'react'
import { useGLTF, useAnimations } from '@react-three/drei'

function Character({ animation = 'idle' }: { animation?: string }) {
  const { scene, animations } = useGLTF('/models/character.glb')
  const { actions } = useAnimations(animations, scene)
  useEffect(() => {
    actions[animation]?.reset().fadeIn(0.3).play()
    return () => { actions[animation]?.fadeOut(0.3) }
  }, [animation, actions])
  return <primitive object={scene} scale={1.5} />
}
useGLTF.preload('/models/character.glb')

// <Canvas><Suspense fallback={null}><Character /></Suspense></Canvas>
```

`useGLTF` decodes Draco-compressed files out of the box. To get a typed JSX component for a model and a compressed copy, run `npx gltfjsx model.glb --transform --types`; it writes `model-transformed.glb` (Draco, resized webp textures, pruned) and a component.

### Events

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

function InteractiveBox() {
  const [hovered, setHovered] = useState(false)
  const [active, setActive] = useState(false)
  return (
    <mesh
      scale={active ? 1.5 : 1}
      onClick={(e) => { e.stopPropagation(); setActive(!active) }}
      onPointerOver={() => setHovered(true)}
      onPointerOut={() => setHovered(false)}
    >
      <boxGeometry />
      <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
    </mesh>
  )
}
```

R3F raycasts pointer events onto meshes. Use `e.stopPropagation()` so objects behind the hit do not also fire, and `onPointerMissed` on `Canvas` for clicks on empty space.

### Rendering only when needed

```tsx
<Canvas frameloop="demand">…</Canvas>
// elsewhere: const invalidate = useThree((s) => s.invalidate); invalidate()
```

With `frameloop="demand"` a frame is drawn only when props change or `invalidate()` is called; useful for static configurators on laptops and phones.

## Examples

### Example 1: Product viewer in a Next.js page

**User prompt:** "Show our sneaker.glb on the product page, let people rotate it, and keep the page fast."

Create a `'use client'` component with `<Canvas frameloop="demand" camera={{ position: [0, 0, 3] }}>`, wrap a `<Stage>` or `<Environment preset="studio" />` plus `<OrbitControls makeDefault />` and a `<Suspense fallback={null}>` around the model. Run `npx gltfjsx public/sneaker.glb --transform --types` first and use `sneaker-transformed.glb`. Result: a draggable model that renders frames only while the user interacts, with the file typically 70-90% smaller.

### Example 2: Thousands of identical objects

**User prompt:** "Our star field with 5,000 meshes drops to 15 fps. Fix it."

Replace the 5,000 `<mesh>` elements with one `<Instances limit={5000}>` (from drei) holding a shared geometry and material, and render each star as `<Instance position={…} />`; for pure animation use a single `<instancedMesh args={[undefined, undefined, 5000]}>` and update matrices in `useFrame`. Result: one draw call instead of 5,000, and the frame rate recovers.

## Guidelines

- Everything lives inside `<Canvas>`; DOM elements go outside, or inside it through drei's `<Html>`.
- Do not use `requestAnimationFrame` or React state for per-frame values; use `useFrame` with refs.
- Create geometry, materials and vectors once (module scope or `useMemo`), not on every render or frame.
- Wrap loaders in `<Suspense>`; `useGLTF`, `useTexture` and `useLoader` suspend and cache by URL. R3F disposes objects when they unmount, so do not dispose shared cached assets by hand.
- Use `<AdaptiveDpr>` and `<PerformanceMonitor>` from drei to lower resolution on slow devices.
- Colours and textures are colour-managed (sRGB) by default; set `flat` to drop tone mapping, `linear` to drop colour conversion, only when you know why.
- `args` on a geometry or object are constructor arguments; changing them recreates the object, so animate properties instead.
- R3F can render with `THREE.WebGPURenderer` through an async `gl` factory (see the Canvas docs), but treat it as advanced.
- For plain, non-React 3D use Three.js directly; for physics add `@react-three/rapier`.

Files in this skill

  • SKILL.md4.9 KB
  • _scores.json1.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…