--- name: r3f-loaders description: React Three Fiber asset loading - useGLTF, useLoader, Suspense patterns, preloading. Use when loading 3D models, textures, HDR environments, or managing loading states. --- # React Three Fiber Loaders ## Quick Start ```tsx import { Canvas } from '@react-three/fiber' import { useGLTF, OrbitControls } from '@react-three/drei' import { Suspense } from 'react' function Model() { const { scene } = useGLTF('/models/robot.glb') return } export default function App() { return ( ) } ``` ## useGLTF (Drei) The recommended way to load GLTF/GLB models. ### Basic Usage ```tsx import { useGLTF } from '@react-three/drei' function Model() { const gltf = useGLTF('/models/robot.glb') // gltf contains: // - scene: THREE.Group (the main scene) // - nodes: Object of named meshes // - materials: Object of named materials // - animations: Array of AnimationClip return } ``` ### Using Nodes and Materials ```tsx function Model() { const { nodes, materials } = useGLTF('/models/robot.glb') return ( {/* Use specific meshes */} ) } ``` ### With TypeScript (gltfjsx) Generate typed components using gltfjsx: ```bash npx gltfjsx model.glb --types ``` ```tsx // Generated component import { useGLTF } from '@react-three/drei' import { GLTF } from 'three-stdlib' type GLTFResult = GLTF & { nodes: { Body: THREE.Mesh Head: THREE.Mesh } materials: { Metal: THREE.MeshStandardMaterial Plastic: THREE.MeshStandardMaterial } } export function Model(props: JSX.IntrinsicElements['group']) { const { nodes, materials } = useGLTF('/model.glb') as GLTFResult return ( ) } useGLTF.preload('/model.glb') ``` ### Draco Compression ```tsx import { useGLTF } from '@react-three/drei' function Model() { // Drei automatically handles Draco if the file is Draco-compressed const { scene } = useGLTF('/models/compressed.glb') return } // Or specify Draco decoder path useGLTF.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/') ``` ### Preloading ```tsx import { useGLTF } from '@react-three/drei' // Preload at module level useGLTF.preload('/models/robot.glb') useGLTF.preload(['/model1.glb', '/model2.glb']) function Model() { // Will be instant if preloaded const { scene } = useGLTF('/models/robot.glb') return } ``` ### Processing Loaded Model ```tsx function Model() { const { scene } = useGLTF('/models/robot.glb') useEffect(() => { // Enable shadows on all meshes scene.traverse((child) => { if (child.isMesh) { child.castShadow = true child.receiveShadow = true } }) }, [scene]) return } ``` ## useLoader (Core R3F) For loading any Three.js asset. ### Basic Texture Loading ```tsx import { useLoader } from '@react-three/fiber' import { TextureLoader } from 'three' function TexturedMesh() { const texture = useLoader(TextureLoader, '/textures/color.jpg') return ( ) } ``` ### Multiple Assets ```tsx function MultiTexture() { const [colorMap, normalMap, roughnessMap] = useLoader(TextureLoader, [ '/textures/color.jpg', '/textures/normal.jpg', '/textures/roughness.jpg', ]) return ( ) } ``` ### With Extensions (Draco) ```tsx import { useLoader } from '@react-three/fiber' import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader' import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader' function Model() { const gltf = useLoader(GLTFLoader, '/model.glb', (loader) => { const dracoLoader = new DRACOLoader() dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/') loader.setDRACOLoader(dracoLoader) }) return } ``` ### Progress Callback ```tsx function Model() { const gltf = useLoader( GLTFLoader, '/model.glb', undefined, // extensions (progress) => { console.log(`Loading: ${(progress.loaded / progress.total) * 100}%`) } ) return } ``` ### Preloading ```tsx import { useLoader } from '@react-three/fiber' import { TextureLoader } from 'three' // Preload useLoader.preload(TextureLoader, '/textures/color.jpg') useLoader.preload(TextureLoader, ['/tex1.jpg', '/tex2.jpg']) // Clear cache useLoader.clear(TextureLoader, '/textures/color.jpg') ``` ## Drei Loader Hooks ### useTexture ```tsx import { useTexture } from '@react-three/drei' // Single const texture = useTexture('/texture.jpg') // Array const [color, normal] = useTexture(['/color.jpg', '/normal.jpg']) // Named object (spreads directly to material) const textures = useTexture({ map: '/color.jpg', normalMap: '/normal.jpg', roughnessMap: '/roughness.jpg', }) // With callback for configuration const texture = useTexture('/texture.jpg', (tex) => { tex.wrapS = tex.wrapT = THREE.RepeatWrapping tex.repeat.set(4, 4) }) // Preload useTexture.preload('/texture.jpg') ``` ### useCubeTexture ```tsx import { useCubeTexture } from '@react-three/drei' function EnvMap() { const envMap = useCubeTexture( ['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg'], { path: '/textures/cube/' } ) return ( ) } ``` ### useEnvironment ```tsx import { useEnvironment } from '@react-three/drei' // Preset const envMap = useEnvironment({ preset: 'sunset' }) // Presets: apartment, city, dawn, forest, lobby, night, park, studio, sunset, warehouse // Custom HDR file const envMap = useEnvironment({ files: '/hdri/studio.hdr' }) // Cube map const envMap = useEnvironment({ files: ['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg'], path: '/textures/', }) ``` ### useVideoTexture ```tsx import { useVideoTexture } from '@react-three/drei' function VideoPlane() { const texture = useVideoTexture('/video.mp4', { start: true, loop: true, muted: true, crossOrigin: 'anonymous', }) return ( ) } ``` ### useFont ```tsx import { useFont, Text3D } from '@react-three/drei' // Preload font useFont.preload('/fonts/helvetiker.json') function Text() { return ( Hello ) } ``` ## Suspense Patterns ### Basic Suspense ```tsx import { Suspense } from 'react' function Scene() { return ( }> ) } function Loader() { return ( ) } ``` ### Loading Progress UI ```tsx import { useProgress, Html } from '@react-three/drei' function Loader() { const { active, progress, errors, item, loaded, total } = useProgress() return (

{Math.round(progress)}% loaded

Loading: {item}

) } function App() { return ( }> ) } ``` ### Drei Loader Component ```tsx import { Loader } from '@react-three/drei' function App() { return ( <> {/* HTML loading overlay */} ) } ``` ## Other Model Formats ### OBJ + MTL ```tsx import { useLoader } from '@react-three/fiber' import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader' import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader' function OBJModel() { const materials = useLoader(MTLLoader, '/model.mtl') const obj = useLoader(OBJLoader, '/model.obj', (loader) => { materials.preload() loader.setMaterials(materials) }) return } ``` ### FBX ```tsx import { useFBX } from '@react-three/drei' function FBXModel() { const fbx = useFBX('/model.fbx') return } // Preload useFBX.preload('/model.fbx') ``` ### STL ```tsx import { useLoader } from '@react-three/fiber' import { STLLoader } from 'three/examples/jsm/loaders/STLLoader' function STLModel() { const geometry = useLoader(STLLoader, '/model.stl') return ( ) } ``` ### PLY ```tsx import { useLoader } from '@react-three/fiber' import { PLYLoader } from 'three/examples/jsm/loaders/PLYLoader' function PLYModel() { const geometry = useLoader(PLYLoader, '/model.ply') useEffect(() => { geometry.computeVertexNormals() }, [geometry]) return ( ) } ``` ## Clone for Multiple Instances ```tsx import { useGLTF, Clone } from '@react-three/drei' function Trees() { const { scene } = useGLTF('/models/tree.glb') return ( <> ) } ``` ## Error Handling ```tsx import { useGLTF } from '@react-three/drei' import { ErrorBoundary } from 'react-error-boundary' function ModelWithErrorHandling() { return ( }> }> ) } function FallbackModel() { return ( ) } ``` ## Asset Caching ```tsx import { useGLTF, useTexture } from '@react-three/drei' // Assets are automatically cached by URL // Same URL = same asset instance function Scene() { // These all reference the same cached asset const model1 = useGLTF('/model.glb') const model2 = useGLTF('/model.glb') const model3 = useGLTF('/model.glb') // Clear cache if needed useGLTF.clear('/model.glb') } ``` ## Performance Tips 1. **Preload critical assets**: Avoid loading during interaction 2. **Use Draco compression**: Smaller file sizes 3. **Use LOD models**: Different detail levels for distance 4. **Clone instead of reload**: For multiple instances 5. **Lazy load non-critical**: Load on demand ```tsx // Preload strategy useGLTF.preload('/models/hero.glb') // Critical useTexture.preload('/textures/main.jpg') // Critical function LazyModel({ visible }) { // Only load when visible const { scene } = useGLTF(visible ? '/models/detail.glb' : null) return scene ? : null } ``` ## See Also - `r3f-animation` - Playing loaded animations - `r3f-textures` - Texture configuration - `r3f-materials` - Materials from loaded models