All hooks / Timers
useAnimationFrame
Calls callback on every animation frame while active, with the time since the previous frame. Always calls the latest callback, so it can read current props without restarting the loop. Cancelled on unmount and when active turns false. Browsers pause it in background tabs.
The callback runs outside React rendering: write to a ref, a canvas, or a style directly. Calling setState every frame re-renders 60+ times a second.
Import
import { useAnimationFrame } from '@vincent.ri/react-tils'
// or only this hook
import { useAnimationFrame } from '@vincent.ri/react-tils/useAnimationFrame'Usage
const canvas = useRef<HTMLCanvasElement>(null)
const x = useRef(0)
useAnimationFrame((deltaMs) => {
x.current = (x.current + deltaMs * 0.1) % 300
const ctx = canvas.current?.getContext('2d')
ctx?.clearRect(0, 0, 300, 50)
ctx?.fillRect(x.current, 10, 30, 30)
}, playing)Live demo
Moves without re-rendering; stops when paused.
When to use it
Drawing on a canvas, a game loop, physics, or smoothing a value that follows the pointer.
When not to
CSS transitions, CSS animations, or the Web Animations API (element.animate()) can do it; they can run off the main thread. For work every second or slower, use useInterval.
API
function useAnimationFrame(callback: (deltaMs: number, timeMs: number) => void, active?: boolean): void| Parameter | Description |
|---|---|
callback | Called each frame with deltaMs (0 on the first frame) and the frame timestamp timeMs. Can change every render. |
active | Run while true. Default true. |
Browser support
| Uses | Chrome | Edge | Firefox | Safari | Chrome Android | Safari iOS |
|---|---|---|---|---|---|---|
| requestAnimationFrameWidely available | 24+ | 12+ | 23+ | 7+ | 25+ | 7+ |
First version with support, from MDN's compatibility data (web-features 3.42.0, @mdn/browser-compat-data 8.1.5).
Coming from another library?
Other libraries call this useRafLoop.