useAnimate
useAnimate returns a tuple [scope, animate] for running animations
imperatively from Svelte. scope is a Svelte 5 attachment you spread onto a
parent element with {@attach scope}. The scoped animate accepts the same
overloads as motion’s standalone animate, and resolves string selectors
against scope.current.
<script lang="ts">
import { stagger, useAnimate } from '@humanspeak/svelte-motion'
const [scope, animate] = useAnimate()
const run = () =>
animate('li', { opacity: 1, y: 0 }, { delay: stagger(0.1) })
</script>
<ul {@attach scope}>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
<button onclick={run}>Animate</button><script lang="ts">
import { stagger, useAnimate } from '@humanspeak/svelte-motion'
const [scope, animate] = useAnimate()
const run = () =>
animate('li', { opacity: 1, y: 0 }, { delay: stagger(0.1) })
</script>
<ul {@attach scope}>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
<button onclick={run}>Animate</button>Sequences
Pass an array of [target, keyframes, options?] tuples to compose timed
animations. The at field controls when each segment starts: a number is an
absolute time, a string like '-0.2' offsets relative to the previous segment,
and '<' runs alongside it.
Selectors resolve against scope.current, so any element you want to target
must be a descendant of the element you spread {@attach scope} on. To
choreograph across the list and a sibling, attach the scope to a wrapper that
contains both:
<script lang="ts">
import { stagger, useAnimate } from '@humanspeak/svelte-motion'
const [scope, animate] = useAnimate()
const playIntro = () =>
animate(
[
['li', { opacity: [0, 1], y: [20, 0] }, { delay: stagger(0.08) }],
['button.cta', { scale: [1, 1.05, 1] }, { duration: 0.4, at: '-0.2' }]
],
{ defaultTransition: { ease: 'easeOut' } }
)
</script>
<div {@attach scope}>
<ul>
<li>One</li>
<li>Two</li>
</ul>
<button class="cta">Continue</button>
</div><script lang="ts">
import { stagger, useAnimate } from '@humanspeak/svelte-motion'
const [scope, animate] = useAnimate()
const playIntro = () =>
animate(
[
['li', { opacity: [0, 1], y: [20, 0] }, { delay: stagger(0.08) }],
['button.cta', { scale: [1, 1.05, 1] }, { duration: 0.4, at: '-0.2' }]
],
{ defaultTransition: { ease: 'easeOut' } }
)
</script>
<div {@attach scope}>
<ul>
<li>One</li>
<li>Two</li>
</ul>
<button class="cta">Continue</button>
</div>Awaiting completion
The returned controls are await-able. Resolve when the entire sequence
finishes:
<script lang="ts">
import { useAnimate } from '@humanspeak/svelte-motion'
const [scope, animate] = useAnimate()
const exit = async () => {
await animate('li', { opacity: 0, y: -20 }, { duration: 0.3 })
// safe to unmount, navigate, fetch the next page, etc.
}
</script><script lang="ts">
import { useAnimate } from '@humanspeak/svelte-motion'
const [scope, animate] = useAnimate()
const exit = async () => {
await animate('li', { opacity: 0, y: -20 }, { duration: 0.3 })
// safe to unmount, navigate, fetch the next page, etc.
}
</script>Cleanup
The attachment cleanup runs when the parent element detaches. Every animation
started through the scoped animate is stopped and scope.animations is
cleared, so animations don’t leak across unmount or HMR boundaries.
When to reach for useAnimate
- Multi-target choreography that’s awkward to express declaratively — sequenced reveals, exit animations gated on user actions, complex staggered effects.
- Animating elements you don’t own as
motion.*components — third-party components, portaled DOM, or content rendered by a child.
For state-driven animations on a single element, the declarative <motion.div animate={...}> API is usually a better fit.
API Reference
Returns
[scope, animate]
scope— a Svelte 5 attachment ((node) => cleanup) with these properties:scope.current: HTMLElement | undefined— the attached element, populated once{@attach scope}fires.scope.animations: AnimationPlaybackControlsWithThen[]— in-flight animations started through the scopedanimate. Cleared automatically when the parent detaches.
animate(target, keyframes, options?)— same overloads as motion’s standaloneanimate. Strings are resolved againstscope.current. Also accepts[ [target, keyframes, options], ... ]sequences with optionalSequenceOptions(see motion docs).
animate returns an AnimationPlaybackControlsWithThen. It’s await-able
and exposes play, pause, stop, cancel, complete, time, speed,
and a finished promise.
See also
- animate — the underlying imperative
API that powers
useAnimate. - stagger — helper that produces per-element delays for selector-based animations.
Based on Motion’s useAnimate API.