Skip to content

Basic Animations

Fresh

This tutorial will assume some React knowledge. We will build a small, continuous animation loop — the basic building block of more advanced animations.

useFrame

useFrame is a Fiber hook that lets you execute code on every frame of Fiber's render loop.

WARNING

Fiber hooks can only be called inside a <Canvas /> parent!

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

function MyAnimatedBox() {
  useFrame(() => {
    console.log("Hey, I'm executing every frame!")
  })
  return (
    <mesh>
      <boxGeometry />
      <meshBasicMaterial color="royalblue" />
    </mesh>
  )
}

The callback we pass to useFrame will be executed every frame and passed an object containing the state of our Fiber scene.

We can extract time information from the clock parameter:

jsx
useFrame(({ clock }) => {
  const a = clock.elapsedTime
  console.log(a) // grows each frame from 0 at scene initialization
})

clock is a three.js Clock object. We get the total elapsed time, which is key for our animations.

Animating with Refs

Instead of routing updates through React state (which is slow), directly mutate the mesh each frame. First, get a reference to it via useRef:

jsx
import React from 'react'

function MyAnimatedBox() {
  const myMesh = React.useRef()
  return (
    <mesh ref={myMesh}>
      <boxGeometry />
      <meshBasicMaterial color="royalblue" />
    </mesh>
  )
}

myMesh will now hold a reference to the actual three.js object, which we can freely mutate in useFrame:

jsx
useFrame(({ clock }) => {
  myMesh.current.rotation.x = clock.elapsedTime
})

What is happening here:

  • We destructure clock from the argument passed to useFrame
  • We access the rotation.x property of myMesh.current — a reference to our actual mesh object
  • We assign a time-dependent value to the rotation on the x axis
  • Our object will now infinitely rotate based on elapsed time

Exercise

Try Math.sin(clock.elapsedTime) and see how your animation changes — the mesh will oscillate back and forth instead of continuously rotating.

Using Delta Time

Always use frame deltas instead of fixed values so your app runs at the same speed on all devices:

jsx
useFrame((state, delta) => {
  myMesh.current.rotation.x += delta
})

This ensures the animation speed is consistent regardless of the user's display refresh rate (60fps, 120fps, etc).

Next Steps

Now that you understand the basic technique for animating in Fiber, learn how events work in the Events and Interaction tutorial.

For deeper animation work:

  • react-spring — spring-physics-based animations that integrate with R3F
  • framer-motion-3d — framer motion for three.js scenes