Composables

useAnimate

Animate CSS properties, transforms, SVG and JS objects with AnimeJS animate().
Instant play

useAnimate wraps the animate function from AnimeJS. It animates DOM elements, SVG nodes, and JavaScript objects.

When reactive parameters change, nanime rebuilds the underlying animation. By default it restarts. Set keepTime: true to carry the playhead over so playback continues instead.

Usage

{"x":0,"y":0}

import { round } from '#nanime/utils'

const vector2D = shallowReactive({ x: 0, y: 0 })

useAnimate(vector2D, {
  x: 100,
  y: 150,
  duration: 10000,
  modifier: round(0),
  ease: 'outElastic',
  alternate: true,
  loop: true,
})
View on GitHub

Signature

function useAnimate(
  target: AnimeTargets,
  parameters?: MaybeRefOrGetter<AnimationParams>,
  options?: NanimeInstanceOptions
): JSAnimation

Parameters

target
AnimeTargets required
Target element, SVG node, plain JavaScript object, or array of targets. Accepts CSS selectors, DOM elements, and component or template refs.
parameters
MaybeRefOrGetter<AnimationParams>
Accepts a plain object, a ref, a reactive() object or a getter. The animation rebuilds when it changes. See the AnimeJS animation documentation for the options.
options
NanimeInstanceOptions
How the animation behaves when it rebuilds.
OptionTypeDefault
keepTimebooleanfalse

Returns

Returns a reactive proxy around the AnimeJS JSAnimation. Its methods and properties are in the AnimeJS animation documentation. Call methods on the returned object, as in animation.play(), because a destructured method throws. Properties like progress don't update a template either.

Caveats

Plain object targets must be reactive or shallowReactive

// wrong: AnimeJS writes to the object's properties directly, bypassing .value
const position = ref({ x: 0, y: 0 })
useAnimate(position, { x: 100 })

// right: AnimeJS animates reactive object properties directly
const position = reactive({ x: 0, y: 0 })
useAnimate(position, { x: 100 })

Passing a ref holding an object fails because AnimeJS writes directly to properties on the target without touching .value. The mutations bypass Vue's reactivity system and the template never updates. Wrap plain objects in reactive or shallowReactive instead so property updates trigger re-renders.

Stagger a list that grows through a getter

// wrong: the stagger works out its steps for the first list only
useAnimate(items, { y: -16, delay: stagger(80), loop: true })

// right: a getter makes a new stagger for every rebuild
useAnimate(items, () => ({ y: -16, delay: stagger(80), loop: true }), { keepTime: true })

AnimeJS stagger() works out each item's step the first time it runs and keeps them. Adding an item rebuilds the animation with the same parameters, so the new item gets the first item's step. A getter creates a fresh stagger on each rebuild, and keepTime: true keeps the others from restarting.

Do not destructure the returned instance

// wrong: play() throws when called
const { play } = useAnimate('.target', { opacity: 0 })

// right
const animation = useAnimate('.target', { opacity: 0 })
animation.play()

The proxy hands back methods without their instance, so a destructured play() runs with no this and throws. A destructured property such as progress is a copy of the value at that moment. Keep the returned object and call methods on it.

Properties do not update templates

// wrong: progress never updates inside a template interpolation
// <p>{{ animation.progress }}</p>

// right: update a ref inside onUpdate
const progress = ref(0)
const animation = useAnimate('.target', {
  opacity: [0, 1],
  onUpdate: self => progress.value = self.progress,
})

Interpolating properties like progress in a template displays only their initial value. AnimeJS updates them outside Vue's reactivity tracking, so nothing triggers a re-render. Assign the current value to a ref inside the onUpdate callback to drive reactive template elements.

Type reference

See also

Copyright © 2026