
R3f Fundamentals
Wire up React Three Fiber scenes, render loops, and Three.js objects in React without hunting scattered R3F docs.
Overview
r3f-fundamentals is an agent skill for the Build phase that teaches React Three Fiber Canvas setup, hooks, JSX elements, events, and refs for Three.js scenes in React.
Install
npx skills add https://github.com/enzed/r3f-skills --skill r3f-fundamentalsWhat is this skill?
- Quick-start RotatingBox pattern with useFrame delta-based rotation on a mesh ref
- Canvas root: perspective and orthographic camera props, gl antialias and renderer options
- Core hooks: useFrame, useThree for loop timing and scene/camera/renderer access
- JSX mapping for mesh, geometry, material, and light elements instead of imperative Three.js
- Pointer events and refs for interacting with 3D objects inside React components
- Documents Canvas camera blocks for perspective fov/near/far and orthographic zoom layouts
- Quick-start snippet covers useFrame, mesh ref, boxGeometry args, and meshStandardMaterial
Adoption & trust: 723 installs on skills.sh; 89 GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
You know React and want 3D in the browser but Canvas options, useFrame, and JSX primitives do not click together from memory.
Who is it for?
Solo builders adding a first R3F scene, hero 3D, or interactive mesh to an existing React or Next.js app.
Skip if: Teams needing advanced shader authoring, physics engines, or production asset pipelines without any React frontend in place.
When should I use this skill?
Setting up R3F scenes, creating components, handling the render loop, or working with Three.js objects in React.
What do I get? / Deliverables
Your agent produces idiomatic R3F components with a working render loop, lights, camera, and mesh refs you can extend into product UI.
- Canvas-wrapped scene with camera and lights
- R3F components using useFrame, refs, and standard mesh JSX
Recommended Skills
Journey fit
3D WebGL UI belongs in the build phase when you are implementing the product surface, not during validation or launch copy. Canvas, JSX mesh primitives, and useFrame hooks are frontend implementation work alongside your React app shell.
How it compares
Use as a focused R3F primer instead of dumping raw Three.js imperative snippets into chat.
Common Questions / FAQ
Who is r3f-fundamentals for?
Indie and solo frontend developers shipping React apps who need a dependable R3F mental model for Canvas, hooks, and mesh JSX.
When should I use r3f-fundamentals?
During Build when setting up scenes, creating R3F components, wiring useFrame, handling events, or attaching refs to Three.js objects in React.
Is r3f-fundamentals safe to install?
Review the Security Audits panel on this Prism page and your org policy before enabling any third-party agent skill in your repo.
SKILL.md
READMESKILL.md - R3f Fundamentals
# React Three Fiber Fundamentals ## Quick Start ```tsx import { Canvas } from '@react-three/fiber' import { useRef } from 'react' import { useFrame } from '@react-three/fiber' function RotatingBox() { const meshRef = useRef() useFrame((state, delta) => { meshRef.current.rotation.x += delta meshRef.current.rotation.y += delta * 0.5 }) return ( <mesh ref={meshRef}> <boxGeometry args={[1, 1, 1]} /> <meshStandardMaterial color="hotpink" /> </mesh> ) } export default function App() { return ( <Canvas camera={{ position: [0, 0, 5], fov: 75 }}> <ambientLight intensity={0.5} /> <directionalLight position={[5, 5, 5]} /> <RotatingBox /> </Canvas> ) } ``` ## Canvas Component The root component that creates the WebGL context, scene, camera, and renderer. ```tsx import { Canvas } from '@react-three/fiber' function App() { return ( <Canvas // Camera configuration camera={{ position: [0, 5, 10], fov: 75, near: 0.1, far: 1000, }} // Or use orthographic orthographic camera={{ zoom: 50, position: [0, 0, 100] }} // Renderer settings gl={{ antialias: true, alpha: true, powerPreference: 'high-performance', preserveDrawingBuffer: true, // For screenshots }} dpr={[1, 2]} // Pixel ratio min/max // Shadows shadows // or shadows="soft" | "basic" | "percentage" // Color management flat // Disable automatic sRGB color management // Frame loop control frameloop="demand" // 'always' | 'demand' | 'never' // Event handling eventSource={document.getElementById('root')} eventPrefix="client" // 'offset' | 'client' | 'page' | 'layer' | 'screen' // Callbacks onCreated={(state) => { console.log('Canvas ready:', state.gl, state.scene, state.camera) }} onPointerMissed={() => console.log('Clicked background')} // Styling style={{ width: '100%', height: '100vh' }} > <Scene /> </Canvas> ) } ``` ### Canvas Defaults R3F sets sensible defaults: - Renderer: antialias, alpha, outputColorSpace = SRGBColorSpace - Camera: PerspectiveCamera at [0, 0, 5] - Scene: Automatic resize handling - Events: Pointer events enabled ## useFrame Hook Subscribe to the render loop. Called every frame (typically 60fps). ```tsx import { useFrame } from '@react-three/fiber' import { useRef } from 'react' function AnimatedMesh() { const meshRef = useRef() useFrame((state, delta, xrFrame) => { // state: Full R3F state (see useThree) // delta: Time since last frame in seconds // xrFrame: XR frame if in VR/AR mode // Animate rotation meshRef.current.rotation.y += delta // Access clock const elapsed = state.clock.elapsedTime meshRef.current.position.y = Math.sin(elapsed) * 2 // Access pointer position (-1 to 1) const { x, y } = state.pointer meshRef.current.rotation.x = y * 0.5 meshRef.current.rotation.z = x * 0.5 }) return ( <mesh ref={meshRef}> <boxGeometry /> <meshStandardMaterial color="orange" /> </mesh> ) } ``` ### useFrame with Priority Control render order with priority (higher = later). ```tsx // Default priority is 0 useFrame((state, delta) => { // Runs first }, -1) useFrame((state, delta) => { // Runs after priority -1 }, 0) // Manual rendering with positive priority useFrame((state, delta) => { // Take over rendering state.gl.render(state.scene, state.camera) }, 1) ``` ### Conditional useFrame ```tsx function ConditionalAnimation({ active }) { useFrame((state, delta) => { if (!active) return /