Navigate

Search topics across all sections

GitHub
Loaders

GLTF Models

Think of a GLTF file like a 3D printer file. When you send an STL to a 3D printer, the file contains the entire model -- shape, structure, everything needed to produce the object. A GLTF file does the same for the web: shapes, colors, textures, and animations, all packaged into one file ready to display in your browser.

You export a model from Blender, drop the 50MB GLB file into your public folder, and load it in your scene. On your development machine it works fine. But when a user on a mobile phone tries to load the page, they wait 30 seconds and then the browser crashes.

terminal
net::ERR_INSUFFICIENT_RESOURCES — Browser ran out of memory while downloading and parsing a 50MB uncompressed GLTF model on a mobile device.

Real-world

Imagine you want to 3D print a robot figurine. You download an STL file that contains the entire robot: the body, the arms, the head, even the tiny gears inside. You send it to the printer and out comes the complete model.

GLTF works the same way for the web. A designer creates a model in Blender or Maya and exports it as a GLB file. That single file contains the geometry (the shape), the materials (the colors and textures), and even animations (walk cycles, idle loops). You load it in your R3F scene and the entire model appears, ready to go.

Blender / Maya

Create the model

Export as GLB

Package everything

Compress

Draco or gltfpack

useLoader

Load in R3F

On Screen!

Model appears

Loading Your First Model

Getting a GLTF model into your scene takes just a few lines. Let's walk through it step by step, from file to screen.

Step 1: Place the file in public

Drop your .glb or .gltf file into the /public/models/ folder. GLB is preferred for production because it is a single binary file. GLTF is a JSON file with separate binary and texture files -- useful for debugging but requires multiple HTTP requests.

Step 2: Load with useLoader

Robot.tsxTSX
import { useLoader } from '@react-three/fiber'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'

function Robot() {
  const gltf = useLoader(GLTFLoader, '/models/robot.glb')
  return <primitive object={gltf.scene} />
}

useLoader downloads the file, parses it, and returns the loaded GLTF object. The primitive component takes the parsed scene graph and drops it directly into your R3F scene.

Step 3: Wrap in Suspense

App.tsxTSX
<Canvas>
  <Suspense fallback={null}>
    <Robot />
  </Suspense>
  <ambientLight />
</Canvas>

useLoader suspends the component while the model downloads. Without Suspense, React does not know what to show during that time. Wrap your model in Suspense so users see a loading indicator (or at least a blank canvas) instead of an error.

Step 4: Enable shadows and traverse

EnableShadows.tsxTSX
gltf.scene.traverse((child) => {
  if (child.isMesh) {
    child.castShadow = true
    child.receiveShadow = true
  }
})

Models do not cast shadows by default. Use traverse to walk through every mesh in the model and enable castShadow and receiveShadow where needed.

What you just learned

GLTF is the standard 3D format for the web -- like a 3D printer file that contains shapes, colors, textures, and animations in one package.

Use useLoader with GLTFLoader to load models, and <primitive> to render them.

Always wrap loader components in <Suspense> to handle the download gracefully.

GLB (binary) is better for production; GLTF (JSON) is better for debugging.

Always compress models with Draco or gltfpack before shipping -- raw exports can be 10x too large.

Question

If a model is just a file containing shapes and materials, what happens when you try to place the same model in three different positions? Can you just render it three times?

Think about it...

You want to place the same tree model at 50 different positions to create a forest. What is the correct approach?

Hint: Remember: in Three.js, a child object can only have one parent...

Try These Challenges

Put what you learned into practice. Try each challenge in the demo above using the Leva controls, then check the solution.

Try This!

Beginner

Change wallColor to red — painted house!

Try This!

Beginner

Toggle showChimney — Santa's route

Try This!

Beginner

Set rotationSpeed to 0 — still life

These are the patterns that trip up developers most often. Switch between Wrong and Fixed to compare the code side by side.

1
Loading huge unoptimized models
Not compressing GLTF models before shipping
Don't do this
CompressedModel.tsxTSX
// 50MB uncompressed model — slow download, crashes mobile
function Model() {
  const gltf = useLoader(GLTFLoader, '/models/scene.glb')
  return <primitive object={gltf.scene} />
}
Raw GLTF files from Blender often contain uncompressed geometry. Draco compression reduces file size by 90% with minimal quality loss. For production, always compress your models. The drei useGLTF hook handles Draco automatically.
2
Reusing the same scene object in multiple places
Three.js objects can only have one parent
Don't do this
CloneModels.tsxTSX
function Forest() {
  const gltf = useLoader(GLTFLoader, '/models/tree.glb')

  return (
    <>
      <primitive object={gltf.scene} position={[-3, 0, 0]} />
      <primitive object={gltf.scene} position={[0, 0, 0]} />
      <primitive object={gltf.scene} position={[3, 0, 0]} />
    </>
  )
}
In Three.js, every Object3D can only have one parent. Passing the same scene to multiple <primitive> components just moves it to the last one. Use drei's <Clone> component or gltf.scene.clone() to create independent copies.
3
No loading state while model downloads
Forgetting to wrap model components in Suspense
Don't do this
SuspenseLoader.tsxTSX
// User sees nothing for 3 seconds
function App() {
  return (
    <Canvas>
      <Model />
    </Canvas>
  )
}
useLoader suspends the component while the asset downloads. Without a Suspense boundary, React has nothing to show during loading. Always wrap loader components in <Suspense> with a fallback so users see a loading indicator instead of a blank screen.

Best Practices

Always Compress

Use Draco or gltfpack compression on all production models. Uncompressed exports can be 10x larger than necessary. The drei useGLTF hook handles Draco decompression automatically.

Use Suspense Boundaries

Always wrap model components in Suspense with a meaningful fallback. Users should see a loading indicator, not a blank screen while models download.

Clone for Reuse

When placing the same model multiple times, use drei's Clone component. Direct reuse of the scene object will just move it to the last position instead of duplicating it.

Prefer useGLTF Over useLoader

The drei useGLTF hook adds automatic Draco support, preloading, and caching over the raw useLoader approach. Use it for all new projects unless you need custom loader configuration.