All hooks / Timers

useAnimationFrame

173 B minified and brotliedSourceMarkdown✓ Works in all modern browsers

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.

frames 0

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
ParameterDescription
callbackCalled each frame with deltaMs (0 on the first frame) and the frame timestamp timeMs. Can change every render.
activeRun while true. Default true.

Browser support

UsesChromeEdgeFirefoxSafariChrome AndroidSafari iOS
requestAnimationFrameWidely available24+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.