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-fiberInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
DesignSupported Platforms
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.
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 foundOur 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.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| threejs-react-three-fiber (this skill)by CloudAI-X | 91 | 3.4k | 4mo ago | SKILL.md |
| algorithmic-artby anthropics | 100 | 177.9k | 30d ago | SKILL.md |
| pptxby anthropics | 100 | 177.9k | 30d ago | SKILL.md |
| designby nextlevelbuilder | 100 | 137.2k | 8d ago | SKILL.md |
| ui-ux-pro-maxby nextlevelbuilder | 100 | 137.2k | 8d ago | SKILL.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.
Skill content
View source on GitHubname: 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 aPerspectiveCamerawith fov 75, near 0.1, far 1000, at z 5 looking at the origin; fiber updates aspect (or left/right/top/bottom withorthographic) and the projection matrix on every resize.camera={{ manual: true }}puts projection entirely in your hands.dpr: a number, or[min, max]clamped againstwindow.devicePixelRatio; the range follows display changes.shadows:trueenables shadow maps, a string ('basic','percentage','variance') picks the filter type, an object merges into the renderer'sshadowMapproperties.true,'soft'and the defaultfalseall 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.flatsets NoToneMapping; without it fiber applies ACESFilmicToneMapping — unlike plain three.js, tone mapping is on by default.linearswitches the output to LinearSRGBColorSpace; the default is sRGB.legacydisables three's ColorManagement.gl: props for the defaultnew 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 orinvalidate(),'never'hands the loop over (call the module-leveladvance(timestamp)export with a time in seconds;useFrame's delta is the difference between successive timestamps, so milliseconds make it 1000 times too large).eventsreplaces the event manager (the default maps DOM pointer events to raycasts);eventSource(an element pointer events attach to, default the Canvas wrapper) andeventPrefix('offset'by default;'page','client','layer','screen'when the page scrolls or transforms the canvas) tune pointer mapping.onCreatedruns once the renderer exists;onPointerMissedfires 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.setDecoderPathretargets it,useGLTF(url, false)disables it).useGLTF.preload(url)warms the cache. The result carriesscene,animations,camerasplusnodes/materials/mesheslookup maps. For several copies of a skinned model, clone it once per component withuseMemo(() => 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
algorithmic-art
177.9kCreating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems.
pptx
177.9kUse this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an em…
design
137.2kComprehensive design skill: brand identity, design tokens, UI styling, logo generation (55 styles, Gemini, Atlas Cloud, or MuAPI AI), corporate identity program (50 deliverables, CIP mockups), HTML presentations (Chart.js), banner design (22 styles, social/ads/web/print), icon design (15 styles, SVG…
ui-ux-pro-max
137.2kUI/UX design intelligence for web, mobile, and desktop. This skill should be used when designing, building, reviewing, or fixing interfaces, including pages, components, design systems, accessibility, interaction, responsive layout, typography, color, charts, and stack-specific UI implementation.
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
