Skip to content

Hooks

Fresh

Hooks allow you to tie or request specific information to your component. All hooks clean up after themselves once the component unmounts.

WARNING

Hooks can only be used inside the Canvas element because they rely on context!

jsx
// WRONG — this will crash
import { useThree } from '@react-three/fiber'

function App() {
  const { size } = useThree() // This will crash
  return (
    <Canvas>
      <mesh>
jsx
// CORRECT — useThree inside Canvas
function Foo() {
  const { size } = useThree()
  ...
}

function App() {
  return (
    <Canvas>
      <Foo />

useThree

This hook gives you access to the state model which contains the default renderer, the scene, your camera, and so on. It also gives you the current size of the canvas in screen and viewport coordinates.

jsx
import { useThree } from '@react-three/fiber'

function Foo() {
  const state = useThree()

The hook is reactive — if you resize the browser for instance, you get fresh measurements.

State Properties

PropDescriptionType
glRendererTHREE.WebGLRenderer
sceneSceneTHREE.Scene
cameraCameraTHREE.PerspectiveCamera
raycasterDefault raycasterTHREE.Raycaster
pointerUpdated, normalized, centric pointer coordinatesTHREE.Vector2
mouseDeprecated — use pointer insteadTHREE.Vector2
clockRunning system clockTHREE.Clock
linearTrue when the colorspace is linearboolean
flatTrue when no tonemapping is usedboolean
legacyDisables global color managementboolean
frameloopRender mode'always', 'demand', 'never'
performanceSystem regression object{ current, min, max, debounce, regress }
sizeCanvas size in pixels{ width, height, top, left }
viewportCanvas viewport size in three.js units{ width, height, initialDpr, dpr, factor, distance, aspect, getCurrentViewport }
xrXR interface{ connect, disconnect }
setSet any state property(state) => void
getRetrieve any state property non-reactively() => GetState<RootState>
invalidateRequest a new render (when frameloop === 'demand')() => void
advanceAdvance one tick (when frameloop === 'never')(timestamp, runGlobalEffects?) => void
setSizeResize the canvas(width, height, top?, left?) => void
setDprSet the pixel-ratio(dpr) => void
setFrameloopSet the current render mode(frameloop?) => void
setEventsSet the event layer(events) => void
onPointerMissedResponse for pointer clicks that missed a target() => void
eventsPointer-event handling{ connected, handlers, connect, disconnect }

Selector

Select specific properties to avoid needless re-renders:

jsx
// Will only trigger re-render when the default camera is exchanged
const camera = useThree((state) => state.camera)
// Will only re-render on resize changes
const viewport = useThree((state) => state.viewport)
// You cannot expect reactivity from three.js internals!
const zoom = useThree((state) => state.camera.zoom)

Reading State Outside the Component Cycle

jsx
function Foo() {
  const get = useThree((state) => state.get)
  ...
  get() // Get fresh state from anywhere you want

Exchanging Defaults

jsx
function Foo() {
  const set = useThree((state) => state.set)
  ...
  useEffect(() => {
    set({ camera: new THREE.OrthographicCamera(...) })
  }, [])

useFrame

This hook allows you to execute code on every rendered frame, like running effects, updating controls, and so on. You receive the state (same as useThree) and a clock delta (in seconds). Your callback is invoked just before a frame is rendered. When the component unmounts it is unsubscribed automatically.

jsx
import { useFrame } from '@react-three/fiber'

function Foo() {
  useFrame((state, delta, xrFrame) => {
    // This function runs at the native refresh rate inside of a shared render-loop
  })

DANGER

Be careful about what you do inside useFrame! You should never setState in there! Keep calculations slim and mind all commonly known pitfalls when dealing with loops, like re-use of variables.

Taking Over the Render Loop

Pass a numerical renderPriority value to disable automatic rendering:

jsx
function Render() {
  // Takes over the render-loop, you are responsible to render
  useFrame(({ gl, scene, camera }) => {
    gl.render(scene, camera)
  }, 1)

function RenderOnTop() {
  // This will execute *after* Render's useframe
  useFrame(({ gl, ... }) => {
    gl.render(...)
  }, 2)

INFO

Callbacks are executed in order of ascending priority values (lowest first, highest last), similar to the DOM's z-order.

Negative Indices

Using negative indices will not take over the render loop, but can be useful for ordering useFrame calls:

jsx
function A() {
  // This will execute first
  useFrame(() => ..., -2)

function B() {
  // This useFrame will execute *after* A's
  useFrame(() => ..., -1)

useLoader

This hook loads assets and suspends for easier fallback and error handling. It can take any three.js loader as its first argument: GLTFLoader, OBJLoader, TextureLoader, FontLoader, etc. It is based on React.Suspense.

jsx
import { Suspense } from 'react'
import { useLoader } from '@react-three/fiber'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'

function Model() {
  const result = useLoader(GLTFLoader, '/model.glb')
  // The result is guaranteed to be present here — useLoader suspends the component
  return <primitive object={result.scene} />
}

function App() {
  return (
    <Suspense fallback={<FallbackComponent /> /* or null */}>
      <Model />
    </Suspense>
  )
}

INFO

Assets loaded with useLoader are cached by default. The URLs given serve as cache keys. This allows you to re-use loaded data everywhere in the component tree.

WARNING

Be very careful with mutating or disposing of loaded assets, especially when you plan to re-use them.

Loader Extensions

Provide a callback as the third argument to configure your loader:

jsx
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'

useLoader(GLTFLoader, url, (loader) => {
  const dracoLoader = new DRACOLoader()
  dracoLoader.setDecoderPath('/draco-gltf/')
  loader.setDRACOLoader(dracoLoader)
})

Loading Multiple Assets at Once

jsx
const [bumpMap, specMap, normalMap] = useLoader(TextureLoader, [url1, url2, url3])

Loading Status

jsx
useLoader(loader, url, extensions, (xhr) => {
  console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
})

Special Treatment of GLTFLoader

If a result.scene prop is found the hook will automatically create an object and material collection: { nodes, materials }.

jsx
const { nodes, materials } = useLoader(GLTFLoader, url)

Pre-Loading Assets

jsx
useLoader.preload(GLTFLoader, '/model.glb' /* extensions */)

useGraph

Convenience hook which creates a memoized, named object/material collection from any Object3D.

jsx
import { useLoader, useGraph } from '@react-three/fiber'

function Model(url) {
  const scene = useLoader(OBJLoader, url)
  const { nodes, materials } = useGraph(scene)
  return <mesh geometry={nodes.robot.geometry} material={materials.metal} />
}