SkillAgentSearch skills...

threejs-react-three-fiber

Building three.js scenes from React with React Three Fiber and drei: renderer setup, the scene graph as JSX, frame-loop, state and loading hooks, pointer events, and adaptive performance, on WebGL or with WebGPU and TSL

Install / Use

npx skills add CloudAI-X/threejs-skills --skill threejs-react-three-fiber

Installs into whichever agent you are using.

About this skill
📄

SKILL.md

Installable skill definition

Quality Score

91/100

Category

Design

Supported Platforms

Universal

Our assessment of threejs-react-three-fiber

threejs-react-three-fiber scores 91/100 on our quality scale, 68th of 256 Design skills we index (top 27%).

Its SKILL.md is 29 KB long, well organised into 13 sections with 16 code examples: a thorough specification that gives an agent plenty to work with.

With 3,371 GitHub stars, it is one of the more widely adopted skills in the catalogue.

Substance
30/30
Structure
20/20
Description
15/15
Adoption
15/20
Freshness
11/15

Maintenance, license and trust

  • The repository was last updated about 4 months ago. That is recent enough to be usable, but agent tooling moves fast, so check the instructions against your agent's current version.
  • No license is declared. By default that means all rights are reserved: you can read it, but reusing or redistributing it is not clearly permitted. Ask the author before building on it commercially.
  • Its trust signals score 86/100, with 1 caution from licensing, adoption, age or documentation. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.

Safety scan

No issues found

Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. An AI review of the same text found nothing harmful.

AI review by kimi-k2.7-code on 2026-10-23. Automated pattern scan on 2026-10-23. It catches known dangerous patterns, not every risk — read a skill before letting an agent act on it.

threejs-react-three-fiber compared with similar skills

All 4 of these similar skills score higher than threejs-react-three-fiber; compare them before choosing.

SkillScoreStarsUpdatedFormat
threejs-react-three-fiber (this skill)by CloudAI-X913.4k4mo agoSKILL.md
algorithmic-artby anthropics100177.9k30d agoSKILL.md
pptxby anthropics100177.9k30d agoSKILL.md
designby nextlevelbuilder100137.2k8d agoSKILL.md
ui-ux-pro-maxby nextlevelbuilder100137.2k8d agoSKILL.md

Frequently asked questions

How do I install threejs-react-three-fiber?
Run npx skills add CloudAI-X/threejs-skills --skill threejs-react-three-fiber. The install tabs above show the steps for each supported agent.
Which AI agents does threejs-react-three-fiber work with?
It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
Is threejs-react-three-fiber safe to use?
Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. An AI review of the same text found nothing harmful. It declares no license and scores 86/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
Is threejs-react-three-fiber still maintained?
The repository was last updated about 4 months ago. That is recent enough to be usable, but agent tooling moves fast, so check the instructions against your agent's current version.

name: threejs-react-three-fiber description: "Building three.js scenes from React with React Three Fiber and drei: renderer setup, the scene graph as JSX, frame-loop, state and loading hooks, pointer events, and adaptive performance, on WebGL or with WebGPU and TSL. Use when writing React components that render three.js."

Three.js React Three Fiber

Targets three.js r186. Examples use WebGLRenderer; the WebGPU and TSL section uses WebGPURenderer.

Quick Start

Targets @react-three/fiber 9.8 with React 19 and @react-three/drei 10.7. The Canvas creates and owns a WebGLRenderer, a scene and a camera; three.js objects become JSX elements. The Canvas fills its parent element, so give the parent a height. The component fragments below render inside a Canvas like this one; lit materials assume its lights.

import { useRef } from "react";
import { Canvas, useFrame } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

function Box() {
  const ref = useRef();
  useFrame((_, delta) => {
    ref.current.rotation.y += delta;
  });
  return (
    <mesh ref={ref} position={[0, 0.5, 0]} castShadow>
      <boxGeometry args={[1, 1, 1]} />
      <meshStandardMaterial color="#d9915b" />
    </mesh>
  );
}

export default function App() {
  return (
    <Canvas shadows="percentage" camera={{ position: [3, 2, 4], fov: 50 }}>
      <color attach="background" args={["#111827"]} />
      <ambientLight intensity={0.4} />
      <directionalLight position={[3, 4, 2]} intensity={2} castShadow />
      <Box />
      <mesh rotation-x={-Math.PI / 2} receiveShadow>
        <planeGeometry args={[10, 10]} />
        <meshStandardMaterial color="#20293a" />
      </mesh>
      <OrbitControls makeDefault />
    </Canvas>
  );
}

Canvas Props

export function App() {
  return (
    <Canvas
      shadows="variance"
      dpr={[1, 2]}
      frameloop="demand"
      flat
      camera={{ position: [0, 2, 6], fov: 45 }}
      gl={{ powerPreference: "high-performance" }}
      onCreated={(state) => state.gl.setClearColor("#111827")}
    >
      {/* scene contents */}
    </Canvas>
  );
}
  • camera: props for the default camera, or a camera instance. Without it you get a PerspectiveCamera with fov 75, near 0.1, far 1000, at z 5 looking at the origin; fiber updates aspect (or left/right/top/bottom with orthographic) and the projection matrix on every resize. camera={{ manual: true }} puts projection entirely in your hands.
  • dpr: a number, or [min, max] clamped against window.devicePixelRatio; the range follows display changes.
  • shadows: true enables shadow maps, a string ('basic', 'percentage', 'variance') picks the filter type, an object merges into the renderer's shadowMap properties. true, 'soft' and the default false all set PCFSoftShadowMap, which three swaps for PCFShadowMap with a console warning (WebGLRenderer when shadows render, WebGPURenderer on every Canvas); 'percentage' is the same soft filter without the warning.
  • flat sets NoToneMapping; without it fiber applies ACESFilmicToneMapping — unlike plain three.js, tone mapping is on by default. linear switches the output to LinearSRGBColorSpace; the default is sRGB. legacy disables three's ColorManagement.
  • gl: props for the default new WebGLRenderer({ canvas, antialias: true, alpha: true, powerPreference: "high-performance" }), a renderer instance, or a factory function (the WebGPU path below).
  • frameloop: 'always' renders every frame, 'demand' renders only after React changes or invalidate(), 'never' hands the loop over (call the module-level advance(timestamp) export with a time in seconds; useFrame's delta is the difference between successive timestamps, so milliseconds make it 1000 times too large).
  • events replaces the event manager (the default maps DOM pointer events to raycasts); eventSource (an element pointer events attach to, default the Canvas wrapper) and eventPrefix ('offset' by default; 'page', 'client', 'layer', 'screen' when the page scrolls or transforms the canvas) tune pointer mapping. onCreated runs once the renderer exists; onPointerMissed fires on clicks that hit nothing.

The Scene Graph as JSX

Every export of the three namespace is an element: lowercase tag, capitalized class (<mesh> → THREE.Mesh, <meshStandardMaterial> → its material). Props apply to the instance; shorthand values work anywhere three has a .set(): position={[1, 2, 3]}, position={new THREE.Vector3(1, 2, 3)}, position={5} (setScalar), color="#d9915b". Dashed props pierce (rotation-x, material-color, geometry-attributes-position); attach="material-0" attaches into an array index.

args is the constructor argument list; changing it rebuilds the object from scratch. Geometries and materials attach to their parent mesh automatically — no attach needed.

function Torus() {
  return (
    <mesh position={[0, 1, 0]}>
      <torusKnotGeometry args={[0.7, 0.25, 128, 24]} />
      <meshStandardMaterial
        color="#42699c"
        roughness={0.2}
        metalness={0.8}
        onUpdate={(self) => console.log(self.type)}
      />
    </mesh>
  );
}

Mount an object you already hold with <primitive object={obj} />; one object per primitive (fiber keeps its per-object state on the object), and clone for copies. Fiber never disposes primitives — their lifetime is yours. dispose={null} on any element opts it out of unmount disposal. Register classes outside the three namespace with extend:

import { extend } from "@react-three/fiber";
import { Water } from "three/addons/objects/Water.js";

extend({ Water });
// then render <water args={[geometry]} /> anywhere in the Canvas

In TypeScript the tags line, audio, source and path collide with DOM types; write threeLine, threeAudio, threeSource, threePath (fiber strips the prefix and resolves the three class).

Hooks

useFrame(callback, renderPriority?) subscribes to the render loop; it runs before the scene renders:

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

function Spinner({ radius = 2 }) {
  const ref = useRef();
  useFrame((state, delta) => {
    ref.current.rotation.y += delta;
    ref.current.position.x = Math.sin(state.clock.elapsedTime) * radius;
  });
  return (
    <mesh ref={ref}>
      <sphereGeometry args={[0.4, 32, 16]} />
      <meshBasicMaterial color="#e8b4a0" />
    </mesh>
  );
}

Subscribers with a positive renderPriority take rendering into their own hands — the Canvas stops rendering, and your callback must call state.gl.render(state.scene, state.camera) itself. At default priority, calling gl.render yourself double-renders.

useThree(selector?) reads Canvas state reactively; with no selector it returns the whole state (gl, camera, scene, raycaster, pointer, size, viewport, invalidate, setDpr, setFrameloop, controls, performance):

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

const camera = useThree((state) => state.camera);
const invalidate = useThree((state) => state.invalidate);

Inside useFrame use the state argument instead — it is the same state without a re-render. state.clock is a THREE.Clock that fiber 9.8 creates, so every Canvas logs three's Clock deprecation warning; it does not come from your code.

useLoader(ClassOrInstance, url, extensions?, onProgress?) suspends until any three loader resolves and caches by loader and URL; its caller must sit under a Suspense boundary. The extensions callback runs on the one loader instance fiber keeps per class, so a setting it makes (a path, a decoder) applies to every later useLoader call with that class. useGraph(object) builds named lookup maps ({ nodes, materials, meshes }) from an object tree:

import { useGraph, useLoader } from "@react-three/fiber";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";

function Model({ url }) {
  const gltf = useLoader(GLTFLoader, url, (loader) => loader.setPath("assets/"));
  const { nodes } = useGraph(gltf.scene);
  return <primitive object={gltf.scene} />;
}

useLoader.preload(Class, url) warms the cache outside React; useLoader.clear(Class, url) drops an entry.

Pointer Events

Objects with handlers are raycast on each pointer event (pointer moves test only objects with move, over, out, enter or leave handlers); handlers fire nearest first, bubbling from the hit object up its parents. The event carries the intersection plus helpers: object, eventObject (the object that registered the handler), point, distance, face, instanceId, intersections, ray, camera, unprojectedPoint, delta (pixels travelled since pointerdown, for click-like events), nativeEvent. Handlers: onClick, onDoubleClick, onContextMenu, onWheel, onPointerUp, onPointerDown, onPointerMove, onPointerOver, onPointerOut, onPointerEnter, onPointerLeave, onPointerMissed (onPointerCancel and onLostPointerCapture are typed but fiber never calls them on objects).

import { useState } from "react";

function Pickable() {
  const [hovered, setHovered] = useState(false);
  return (
    <mesh
      onClick={(event) => {
        event.stopPropagation(); // underlying objects along the ray no longer receive it
        console.log(event.object.name, event.distance, event.point, event.instanceId);
      }}
      onPointerOver={(event) => {
        event.stopPropagation();
        setHovered(true);
      }}
      onPointerOut={() => setHovered(false)}
      onPointerDown={(event) => {
        event.target.setPointerCapture(event.pointerId); // this pointer's events reach the mesh off its surface until release
      }}
      onPointerUp={(event) => {
        event.target.releasePointerCapture(event.pointerId);
      }}
      onPointerMissed={() => console.log("clicked elsewhere")}
    >
      <sphereGeometry args={[0.75, 32, 16]} />
      <meshStandardMaterial color={hovered ? "#e8b4a0" : "#42699c"} />
    </mesh>
  );
}

Pointer events work under any renderer: the default raycaster tests CPU geometry in intersectObject(object, true). Pass raycaster={{ params: { Line: { threshold: 0.1 } } }} to tune it.

Loading Assets

Loading hooks suspend; put the boundary above them and the fallback shows while they load.

import { Suspense } from "react";
import { Canvas } from "@react-three/fiber";
import { useGLTF, useProgress, Environment } from "@react-three/drei";

function Model({ url }) {
  const { scene, animations, nodes, materials } = useGLTF(url);
  return <primitive object={scene} />;
}

function Hud() {
  const { active, progress, errors } = useProgress(); // tracks three's DefaultLoadingManager
  return <div>{active ? `${progress.toFixed(0)}%` : errors.length > 0 ? "failed" : "done"}</div>;
}

export function App() {
  return (
    <>
      <Hud />
      <Canvas camera={{ position: [3, 2, 4], fov: 50 }}>
        <Suspense fallback={null}>
          <Environment preset="city" />
          <Model url="model.glb" />
        </Suspense>
      </Canvas>
    </>
  );
}
  • useGLTF(url) loads through the GLTFLoader drei ships with (three-stdlib), with Draco and meshopt enabled by default; the Draco decoder comes from a pinned gstatic URL (useGLTF.setDecoderPath retargets it, useGLTF(url, false) disables it). useGLTF.preload(url) warms the cache. The result carries scene, animations, cameras plus nodes/materials/meshes lookup maps. For several copies of a skinned model, clone it once per component with useMemo(() => SkeletonUtils.clone(scene), [scene]) (import * as SkeletonUtils from "three/addons/utils/SkeletonUtils.js") and mount each clone in its own primitive.
  • useTexture(input) loads one URL, an array (const [a, b] = useTexture([x, y])) or a keyed object; fiber sets sRGB color space on LDR (RGBA

Truncated for display — read the full file on GitHub.

Related Skills

View on GitHub
GitHub Stars3.4k
CategoryDesign
Updated3mo ago
Forks381

Trust signals

86/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

1 medium1 info