```
The API is nearly identical - just change the import and function name.
## Tips
### Reusing Style Objects
For reusable base styles:
```svelte
```
### Combining with motion Components
Works seamlessly with motion components:
```svelte
Interactive!
```
## See also
- [useTime](/docs/use-time) - Time-based reactive values
- [useTransform](/docs/use-transform) - Transform and map values
- [Examples](/examples) - See styleString in action
# SVG Animation
> Bind MotionValues to SVG presentation attributes, draw paths, and use attrX/attrY/attrScale.
**Source:** [https://motion.svelte.page/docs/svg-animation](https://motion.svelte.page/docs/svg-animation)
---
Any `MotionValue` can drive an SVG presentation attribute directly. Pass it as a
prop and Motion subscribes to it — no `animate` prop, no keyframes, and no
component re-render when the value changes.
```svelte
```
> Live example: [/examples/svg-animation](https://motion.svelte.page/examples/svg-animation)
## Two DOM channels
Motion writes a bound attribute to one of two places, chosen per key at runtime:
- Keys that are **CSS properties** in the browser — `cx`, `cy`, `r`, `rx`, `ry`,
`width`, `height`, `d`, and the `stroke-*` family — are written to
`element.style`.
- Everything else — `points`, `viewBox`, `x1`/`y1`/`x2`/`y2` — is written with
`setAttribute`.
This matters when you inspect the DOM or write a test. A bound `cx` never
changes the element's `cx` **attribute**; it moves the computed style instead.
The attribute you see in devtools is the initial server-rendered value.
```ts
// After cx.set(60):
getComputedStyle(circle).cx // "60px" <- the live value
circle.getAttribute('cx') // "40" <- the SSR seed, frozen
```
Both render identically. CSS geometry properties win over the matching
presentation attribute, which is exactly why the initial attribute can stay put.
## Prop names use the DOM spelling
Svelte templates take the DOM spelling of hyphenated attributes, so write
`stroke-width`, not `strokeWidth`:
```svelte
```
Both spellings are accepted, but the kebab-case form is the one you would write
for a plain `
`, and it is what the rendered markup uses.
## Server rendering
A bound attribute is server-rendered with the MotionValue's current value, so
the first paint is correct and nothing flashes on hydration. Attribute names are
emitted with their DOM spelling — `strokeDashoffset` becomes
`stroke-dashoffset`, while genuinely camelCase names like `viewBox` are left
alone.
## attrX, attrY, and attrScale
`x`, `y`, and `scale` are ambiguous: each is both an SVG attribute and a CSS
transform that Motion already owns. Passing `x` moves the element with a
transform. To reach the **attribute** instead, use `attrX`, `attrY`, or
`attrScale`:
```svelte
```
**`scale` is not a presentation attribute on shape elements.** `attrScale` on a
`` writes the attribute faithfully, and the rect ignores it — nothing
moves. It is a real attribute on ``, where it drives the
displacement amount. This mirrors Framer Motion's behavior.
## Drawing paths
`pathLength`, `pathSpacing`, and `pathOffset` are handled separately, as
normalized 0–1 values. They animate the underlying `stroke-dasharray` and
`stroke-dashoffset` for you:
```svelte
```
Set `pathLength` to `0.5` and half the path is drawn, regardless of its actual
length in user units.
## Filter primitives
SVG tag names are case-sensitive, and motion components are addressed in
lowercase. `motion.fedisplacementmap` renders a correctly-cased
``:
```svelte
```
Filter-primitive attributes are bindable like any other. `stdDeviation`,
`baseFrequency`, `numOctaves`, `dx`, `dy`, and `radius` all take a MotionValue:
```svelte
```
The same holds for `motion.lineargradient`, `motion.clippath`,
`motion.textpath`, and the rest of the camelCase SVG elements.
# transformTemplate
> Customize the generated CSS transform string for motion components.
**Source:** [https://motion.svelte.page/docs/transform-template](https://motion.svelte.page/docs/transform-template)
---
`transformTemplate` lets a motion component customize the final CSS
`transform` string that Motion writes to the element.
Motion still builds the generated transform from shortcuts like `x`, `y`,
`scale`, and `rotate`. Your template receives both the latest transform values
and the generated string, then returns the transform that should be rendered.
```svelte
```
> Live example: [/examples/transform-template](https://motion.svelte.page/examples/transform-template)
## Callback Shape
```ts
type TransformTemplate = (
transform: Record,
generatedTransform: string
) => string
```
- `transform` contains the latest transform shortcut values with CSS units
applied. For example, `x: 10` is passed to the template as `"10px"`, and
`rotate: 45` is passed as `"45deg"`.
- `generatedTransform` is the string Motion would normally write, such as
`"translateX(10px) rotate(45deg)"`.
- The returned string becomes the element's final inline `transform`.
## MotionValue Styles
`transformTemplate` also works with MotionValues inside object-form styles:
```svelte
`skewY(-2deg) ${generated}`}
/>
```
When `x` changes, the generated transform and the templated transform update
together.
## API Reference
### `transformTemplate`
```ts
transformTemplate?: TransformTemplate
```
Pass a callback to any `motion.*` component. The callback receives latest
transform shortcut values and the generated transform string.
Based on [Motion's transformTemplate prop](https://motion.dev/docs/react-motion-component#transformtemplate).
# Tree Shaking
> Reduce bundle size by importing only the motion components you use
**Source:** [https://motion.svelte.page/docs/tree-shaking](https://motion.svelte.page/docs/tree-shaking)
---
The `motion` object (`motion.div`, `motion.span`, etc.) provides a convenient API that mirrors Framer Motion, but it bundles all 170+ HTML and SVG element wrappers regardless of how many you actually use. For production apps where bundle size matters, svelte-motion offers three ways to import only what you need.
## The problem
When you use the `motion` object, bundlers cannot determine which properties are accessed at build time:
```svelte
Click me
```
This is because JavaScript object property access is a runtime operation that static analysis cannot optimize.
## Option 1: Named exports
The simplest approach. Import components by name and they tree-shake automatically with any bundler:
```svelte
Click me
```
Every HTML and SVG element has a corresponding `Motion`-prefixed export. The naming convention is `Motion` + the PascalCase element name:
| Element | Named export |
|---|---|
| `div` | `MotionDiv` |
| `button` | `MotionButton` |
| `span` | `MotionSpan` |
| `a` | `MotionA` |
| `svg` | `MotionSvg` |
| `circle` | `MotionCircle` |
| `path` | `MotionPath` |
| `h1`...`h6` | `MotionH1`...`MotionH6` |
| `input` | `MotionInput` |
| `img` | `MotionImg` |
All other exports (`animate`, `useSpring`, `AnimatePresence`, types, etc.) continue to work alongside named component imports:
```svelte
```
## Option 2: Vite plugin
If you prefer the `motion.div` syntax, the `svelteMotionOptimize` Vite plugin automatically rewrites it into direct imports at build time. Your source code stays unchanged:
```svelte
Hello
Hello
```
### Setup
Add the plugin to your `vite.config.ts` before the SvelteKit plugin:
```ts
import { svelteMotionOptimize } from '@humanspeak/svelte-motion/vite'
import { sveltekit } from '@sveltejs/kit/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
svelteMotionOptimize(),
sveltekit()
]
})
```
No changes to your `.svelte` files are needed. The plugin handles:
- Opening and closing tags: `` ... ` `
- Self-closing tags: ` `
- Script references: `const C = motion.div`
- Mixed imports: preserves other named imports like `animate`, `useSpring`
- SVG elements: `motion.circle`, `motion.path`, etc.
## Option 3: Direct imports
For maximum control, import components directly from their `.svelte` files:
```svelte
Click me
```
This bypasses the package entry point entirely and works with any bundler.
## Which approach to choose
| | Named exports | Vite plugin | Direct imports |
|---|---|---|---|
| **Setup** | None | One line in vite.config | None |
| **Syntax** | `` | `` | `` |
| **Bundler support** | All | Vite only | All |
| **Tree-shakes** | Yes | Yes | Yes |
| **Autocomplete** | Full | Full | Per-import |
**Recommended:** Use **named exports** for new projects. They work everywhere, require no configuration, and provide full autocomplete. Use the **Vite plugin** if you're migrating an existing codebase and want to keep the `motion.div` syntax.
## Related
- [Get started](/docs) — Motion component overview
- [AnimatePresence](/docs/animate-presence) — Exit animations
- [Variants](/docs/variants) — Named animation states
# useAnimate
> Imperative animation with a scoped CSS-selector API.
**Source:** [https://motion.svelte.page/docs/use-animate](https://motion.svelte.page/docs/use-animate)
---
`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`.
```svelte
Animate
```
> Live example: [/examples/use-animate](https://motion.svelte.page/examples/use-animate)
## 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:
```svelte
```
## Awaiting completion
The returned controls are `await`-able. Resolve when the entire sequence
finishes:
```svelte
```
## 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
`
` 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 scoped `animate`. Cleared automatically when
the parent detaches.
- **`animate(target, keyframes, options?)`** — same overloads as
motion's standalone `animate`. Strings are resolved against
`scope.current`. Also accepts `[ [target, keyframes, options], ... ]`
sequences with optional `SequenceOptions` (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](https://motion.dev/docs/animate) — the underlying imperative
API that powers `useAnimate`.
- [stagger](https://motion.dev/docs/stagger) — helper that produces
per-element delays for selector-based animations.
---
Based on [Motion's useAnimate](https://motion.dev/docs/react-use-animate) API.
# useAnimationControls
> Legacy imperative controls for coordinating one or more motion components.
**Source:** [https://motion.svelte.page/docs/use-animation-controls](https://motion.svelte.page/docs/use-animation-controls)
---
`useAnimationControls` creates a legacy imperative controller. Pass the returned
object to one or more `motion.*` components via `animate={controls}`, then call
`controls.start(...)`, `controls.set(...)`, or `controls.stop()`.
```svelte
Start
```
> Live example: [/examples/use-animation-controls](https://motion.svelte.page/examples/use-animation-controls)
## Sequencing
`controls.start(definition)` returns a promise. If the controls are subscribed
to three motion components, that promise resolves after all three components
finish their animation.
```svelte
```
## Variants
Each subscribed component resolves the same variant label against its own
`variants` map. This mirrors Motion's VisualElement fan-out behavior:
```svelte
```
## API Reference
### `useAnimationControls()`
Returns an `AnimationControls` object:
- **`start(definition, transitionOverride?)`** — animates every subscribed
component and returns `Promise`.
- **`set(definition)`** — synchronously sets every subscribed component to the
target's final values. Variant labels and `transitionEnd` values are resolved.
- **`stop()`** — stops active subscriber animations.
- **`mount()`** — internal lifecycle hook called automatically by
`useAnimationControls()`.
- **`subscribe(subscriber)`** — internal component subscription hook.
`start` and `set` throw if called before the hook's component has mounted,
matching upstream Motion's guard against render-synchronous control calls.
## Alias
`useAnimation` is exported as an alias of `useAnimationControls`, matching
Motion's legacy API.
## Related
- [`useAnimate`](/docs/use-animate) — scoped selector-based imperative animation
- [`Variants`](/docs/variants) — named animation states
Based on [Motion's useAnimationControls](https://motion.dev/docs/react-use-animation-controls) API.
# useAnimationFrame
> Run a callback on every animation frame for smooth, time-based animations.
**Source:** [https://motion.svelte.page/docs/use-animation-frame](https://motion.svelte.page/docs/use-animation-frame)
---
`useAnimationFrame` runs a callback once every animation frame, providing direct access to the browser's animation loop.
```svelte
Rotating content
```
## Usage
The callback receives a `DOMHighResTimeStamp` representing the time elapsed since the time origin, in milliseconds.
```svelte
Animated content
```
> Live example: [/examples/use-animation-frame](https://motion.svelte.page/examples/use-animation-frame)
## How it works
`useAnimationFrame` provides a simple way to create time-based animations by:
1. Starting a `requestAnimationFrame` loop when called
2. Calling your callback with the current timestamp on each frame
3. Returning a cleanup function that stops the animation loop
### With $effect
For Svelte 5, wrap `useAnimationFrame` in a `$effect` and return its cleanup function. This ensures the animation loop is properly stopped when the component unmounts:
```svelte
```
## Performance
`useAnimationFrame` is optimized for performance:
- **Native timing**: Uses `requestAnimationFrame` which runs at the optimal frame rate (typically 60 FPS)
- **Automatic cleanup**: Cancels animation frames when the effect is destroyed
- **SSR-safe**: Returns a no-op function in server-side rendering environments
## Common patterns
### Smooth rotation
```svelte
```
### Pulsing animation
```svelte
```
### Oscillating movement
```svelte
```
## API Reference
### Parameters
The callback function receives one argument:
- **time** `number` - The total milliseconds since the animation started (DOMHighResTimeStamp)
### Returns
A cleanup function `() => void` that stops the animation loop when called.
## When to use
Use `useAnimationFrame` when you need:
- Frame-by-frame control over animations
- Time-based calculations for smooth motion
- Complex animation logic that depends on elapsed time
- Direct DOM manipulation for performance-critical animations
For declarative animations, consider using the `motion` component with `animate` props instead.
## See also
- [useTime](/docs/use-time) - For reactive time stores
- [Motion component](/docs) - For declarative animations
- [Examples](/examples) - See useAnimationFrame in action
---
Based on [Motion's useAnimationFrame](https://motion.dev/docs/react-use-animation-frame) API.
# useCycle
> Cycle through a series of values to drive variants, properties, or any state machine.
**Source:** [https://motion.svelte.page/docs/use-cycle](https://motion.svelte.page/docs/use-cycle)
---
`useCycle` cycles through a series of values. It pairs naturally with `motion` variants or any prop you want to toggle on user interaction.
```svelte
x.cycle()} />
```
`useCycle` returns a `{ current, cycle }` object:
- `current` is a Svelte 5 reactive getter backed by `$state` — read it in templates, `$derived`, or `$effect` and it tracks automatically.
- `cycle()` advances to the next item, wrapping back to the first when it passes the end.
- `cycle(i)` jumps directly to the value at index `i`.
> **Three deliberate divergences from React framer-motion's `useCycle`:**
>
> 1. **Return shape** — React returns `[value, cycle]`. Under Svelte 5 runes, destructuring a `$state`-backed value snapshots it and loses reactivity, so we return an object whose getter preserves tracking.
> 2. **Out-of-range reads always clamp** instead of returning `items[i]` undefined — see [API Reference → Notes](#notes) below.
> 3. **`cycle(next)` requires an integer index** and throws on `NaN`, `Infinity`, or fractional values. React would silently make `.current` resolve to `undefined`; we surface the bug at write time.
>
> Otherwise 1:1 with React, including same-index no-op bail-out and `wrap(0, length, index + 1)` advance semantics.
> Live example: [/examples/use-cycle](https://motion.svelte.page/examples/use-cycle)
## Cycling through variants
The most common use is toggling between named variants on a `motion` component. Pass the variant names to `useCycle` and bind `.current` to the `animate` prop:
```svelte
variant.cycle()}
>
Cycle
```
## Jumping to a specific index
Pass a number to jump directly to that index instead of advancing one step. Subsequent `cycle()` calls advance from the new position:
```svelte
```
## Cycling through object items
`useCycle` preserves referential identity when cycling through objects, so animation targets, variant definitions, and other complex values stay stable:
```svelte
target.cycle()} />
```
## Reactive items
The varargs form captures `items` once at call time. If your list of items can change — for example, it comes from a prop, a `$state`, or a `$derived` — pass a getter function instead. The cycle re-reads through the getter on every access, so list changes propagate automatically:
```svelte
variant.cycle()} />
```
This mirrors React framer-motion's behavior where `useCycle(...items)` re-binds on every render: when `items` changes, the cycle picks up the new list while keeping the same internal index. If the new list is shorter than the current index, `.current` clamps to the last valid item rather than returning `undefined`.
> The getter form is detected when `useCycle` is called with a single function argument. To cycle through a single function value (uncommon), use `useCycle(() => [fn])` — but a single-item cycle is a no-op anyway, so this rarely matters.
## API Reference
### Parameters
Two call forms:
- **Varargs** — `useCycle(...items: T[])` — captures items at construction time. Matches React framer-motion's signature.
- **Reactive getter** — `useCycle(getItems: () => readonly T[])` — re-reads items on every access so reactive sources propagate.
Must resolve to at least one item or `useCycle` throws.
### Returns
A `CycleState` object:
- **`current`** `T` (getter) — the current item, starting at `items[0]`. Tracks reactively via `$state`. Clamps to the last valid index if items shrink underneath.
- **`cycle(next?: number)`** — advance to the next item, or jump to the value at index `next` when supplied. No-ops if the resolved index is unchanged.
### Notes
- Out-of-range indexes are stored as-given by `cycle(i)` but `.current` always clamps on read — `x.cycle(99)` on a 3-item cycle yields `items[2]`, `x.cycle(-5)` yields `items[0]`. This is a defensive divergence from React framer-motion (which returns `items[i]`, possibly undefined) so `.current` always honors its `T` type. Subsequent `cycle()` advances are relative to the *stored* (unclamped) index, so `cycle(99); cycle()` on a 3-item cycle wraps via `wrap(0, 3, 100) = 1`.
- If the reactive getter form's items list empties mid-cycle, `.current` throws (`useCycle items getter returned an empty list`). The public type is `T`, so silently returning `undefined` there would be a lie — surfacing the bug loudly is the better trade.
- `cycle(next)` throws (`useCycle index must be a finite integer`) when `next` is `NaN`, `Infinity`, or a fractional number. Those slip past the read-time clamp (NaN comparisons return false for both `< 0` and `>= length`) and would otherwise silently make `.current` resolve to `undefined`.
- Same-index calls are no-ops and don't trigger downstream reactivity, matching React `useState`'s `Object.is` bail-out.
- File name is `cycle.svelte.ts` because the hook uses `$state` at module scope; if you reach into the source, the `.svelte.ts` extension is required for runes to compile.
## See also
- [Variants](/docs/variants) — declarative animation states that pair naturally with `useCycle`.
- [AnimatePresence](/docs/animate-presence) — coordinate enter/exit animations as cycled state changes.
---
Based on [Motion's useCycle](https://motion.dev/docs/react-use-cycle) API.
# useFollowValue
> Create a MotionValue that animates to its latest value using any transition type — spring, tween, inertia, or keyframes.
**Source:** [https://motion.svelte.page/docs/use-follow-value](https://motion.svelte.page/docs/use-follow-value)
---
`useFollowValue` is the generalised follow hook. Where [`useSpring`](/docs/use-spring) is locked to spring physics, `useFollowValue` accepts **any** transition type — spring, tween, inertia, or keyframes — so a single source can drive multiple followers, each with its own personality.
```svelte
x.set(x.get() === 0 ? 200 : 0)}>Toggle
eased follow
```
Returns a real motion-dom `MotionValue` augmented with a `$state`-backed `.current` getter and a Svelte readable `.subscribe` shim — same shape every other Tier 2 hook returns.
> Live example: [/examples/use-follow-value](https://motion.svelte.page/examples/use-follow-value)
The example above drives **six** followers from a single source (the pointer position). Each gets a different transition — crisp spring, bouncy spring, floaty heavy spring, quick tween, long lazy tween, and a deliberately under-damped wobbly spring — so the trail visually demonstrates how each transition feels. (Inertia is shown separately in [its own section](#inertia) — it's the right transition for fling-and-release, not continuous follow.)
## Relationship to `useSpring`
`useSpring(source, options)` is now a thin wrapper that calls `useFollowValue(source, { type: 'spring', ...options })`. Use whichever reads better:
```ts
// Equivalent — both produce a spring-animated MotionValue.
const a = useSpring(0, { stiffness: 200 })
const b = useFollowValue(0, { type: 'spring', stiffness: 200 })
```
Reach for `useFollowValue` when you want a non-spring transition or when you're building something that mixes transition types (the example above is the canonical case).
## Transition types
`useFollowValue`'s options accept any motion-dom transition shape. The four built-in types:
### Spring (default)
```svelte
```
All `SpringOptions` are accepted: `stiffness`, `damping`, `mass`, `velocity`, `restDelta`, `restSpeed`, `duration`, `visualDuration`, `bounce`. See [`useSpring`](/docs/use-spring#options) for the per-option semantics.
### Tween
```svelte
```
`ease` accepts a string (`'linear'`, `'easeIn'`, `'easeOut'`, `'easeInOut'`, `'circIn'` / `'circOut'` / `'circInOut'`, `'backIn'` / `'backOut'` / `'backInOut'`, `'anticipate'`), a function `(t: number) => number`, or a cubic-bezier array `[x1, y1, x2, y2]`.
### Inertia
```svelte
```
`inertia` decays toward the target with no overshoot. Useful for scroll-rest, fling animations, and floaty-but-stable trails.
### Keyframes
```svelte
```
Use when you want a follower that animates through a specific path on every source change rather than directly toward the new value.
## With Svelte readables
Like `useSpring`, `useFollowValue` accepts a Svelte `Readable` as the source via an internal bridge. Useful when the source is a `writable` store or a hook that still returns a Svelte readable:
```svelte
target.set(200)}>set 200
```
## SSR
`useFollowValue` is SSR-safe. On the server it returns a static `MotionValue` with no animation; `.set` and `.jump` become no-ops so the initial value matches the server-rendered snapshot.
## API Reference
### Parameters
- **source** `number | string | MotionValue | Readable` — initial value or a source to follow.
- **options** `UseFollowValueOptions` (optional) — `ValueAnimationTransition` of any type (`spring` / `tween` / `inertia` / `keyframes`) plus `skipInitialAnimation` to suppress the first animation when following a hot source (useful for `useScroll`-restoration scenarios).
### Returns
A `FollowMotionValue` — a real motion-dom `MotionValue` (composes with `useTransform`, `useVelocity`, `animate()`, etc.) plus:
- `.current` — Svelte 5 reactive getter (templates / `$derived` / `$effect`).
- `.get()` — imperative current-value read.
- `.set(v)` — animate toward `v` using the configured transition.
- `.jump(v)` — set immediately without animation.
- `.subscribe(run)` — Svelte readable store contract.
- `.on(event, cb)` — motion-dom event bus (`'change'`, `'animationStart'`, `'animationComplete'`).
- All other `MotionValue` methods.
## See also
- [`useSpring`](/docs/use-spring) — convenience wrapper with `type: 'spring'` baked in
- [`useMotionValue`](/docs/motion-values) — the base motion-value primitive (no follow / no animation)
- [Motion values overview](/docs/motion-values) — how the augmented MotionValue shape composes
---
Based on [Motion's useFollowValue](https://motion.dev/docs/react-use-follow-value) API.
# useInView
> Track whether an element is in the viewport via a $state-backed { current } snapshot.
**Source:** [https://motion.svelte.page/docs/use-in-view](https://motion.svelte.page/docs/use-in-view)
---
`useInView` returns a `$state`-backed `{ current }` object reporting whether an element is in the viewport. It uses the same `IntersectionObserver` infrastructure as the `whileInView` motion prop, so it's a good fit for non-animation side effects: analytics impressions, lazy data loads, one-shot reveals, and scroll-driven UI.
```svelte
{inView.current ? 'visible' : 'hidden'}
```
> Diverges from React framer-motion's plain `boolean` return for the same reason as `useCycle`: a `$state`-backed value must live on an object so reads inside getters preserve tracking under Svelte 5 runes.
`target` accepts either an `HTMLElement` directly or a getter
`() => HTMLElement | undefined`. The getter form is the right choice with
Svelte 5 `bind:this`, because the element binding isn't available until after
mount — the hook resolves it lazily and polls on `requestAnimationFrame`
until it appears.
> Live example: [/examples/use-in-view](https://motion.svelte.page/examples/use-in-view)
## Latching with `once`
Pass `{ once: true }` when you only care about the first viewport entry. `.current` flips to `true` and the observer stops — subsequent scrolls don't flip it back:
```svelte
…
```
## Custom thresholds and roots
`amount` controls how much of the element must be visible to count as
"in view": `"some"` (default, any pixel), `"all"` (fully visible), or a number
between `0` and `1`. `margin` is forwarded to `IntersectionObserver`'s
`rootMargin`, and `root` lets you observe inside a scrollable container instead
of the viewport.
```svelte
```
## How it works
- Subscribes to `motion`'s `inView()` primitive, which is also used by `whileInView` — one IntersectionObserver implementation, two consumers.
- The observer is bound to the component's lifecycle via `$effect`: it attaches at mount, detaches at unmount. Subscriber counts no longer drive lifecycle (unlike the pre-runes impl).
- Returns a static `{ current: options.initial ?? false }` when `window` or `IntersectionObserver` is unavailable, so server rendering is safe.
## API Reference
### Parameters
- **target** `HTMLElement | (() => HTMLElement | undefined)` — the element to observe.
- **options** `UseInViewOptions` (optional)
### `UseInViewOptions`
| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| `root` | `HTMLElement \| () => HTMLElement` | viewport | Scroll container to observe inside. |
| `margin` | `string` | `'0px'` | CSS margin around the root bounding box (passed to `rootMargin`). |
| `amount` | `'some' \| 'all' \| number` | `'some'` | Fraction of the target that must be visible. |
| `once` | `boolean` | `false` | When `true`, latches `true` on first entry and stops observing. |
| `initial` | `boolean` | `false` | Value emitted before the first IntersectionObserver callback. |
### Returns
An `InViewState` object:
- **`current`** `boolean` (getter) — `true` while the target is in view, `false` otherwise. Reactive via `$state`.
- **`subscribe(run)`** — Svelte readable store contract. Synchronously emits the current value, then re-emits on every change. Kept for compat with hooks that still consume Svelte readables.
## See also
- The `whileInView` motion prop — declarative animation when a
`motion.*` element enters the viewport. Both this hook and `whileInView`
share the same `IntersectionObserver` infrastructure under the hood.
- [useScroll](/docs/use-scroll) — scroll position stores for scroll-driven
animations.
---
Based on [Motion's useInView](https://motion.dev/docs/react-use-in-view) API.
# useMotionTemplate
> Compose a MotionValue from multiple motion values using a tagged template literal.
**Source:** [https://motion.svelte.page/docs/use-motion-template](https://motion.svelte.page/docs/use-motion-template)
---
`useMotionTemplate` is a tagged template literal that builds an augmented `MotionValue` from interpolated motion values (or Svelte readables). When any input emits, the template is recomposed.
```svelte
Content
```
## Usage
Pass motion values (or any Svelte readable) as template interpolations. Static strings and current values are interleaved to produce the result.
```svelte
Dynamic shadow
```
### Multiple stores
You can interpolate as many stores as needed:
```svelte
Reactive color
```
### With useTransform
Combine with `useTransform` to map values before composing:
```svelte
Animated filter
```
## How it works
1. The tagged template function receives the static string parts and the interpolated inputs (motion values or Svelte readables)
2. Seeds the result motion value by sampling each input once via `sampleSource`
3. Subscribes to every input. The first synchronous emit from each input is skipped (we've already seeded) — subsequent emits recompose the template and write to the result via `result.set(...)`
4. All subscriptions and the result motion value are torn down when the surrounding `$effect` scope unmounts
## Performance
- **Subscription-driven**: Recomposes only when an input emits, no animation frames or polling
- **Single allocation per recompose**: The static-parts array is constant; only the composed string is rebuilt
- **SSR-safe**: Returns a static augmented motion value composed from the seed snapshots on the server
## Common patterns
### Dynamic CSS filter
```svelte
```
### Animated gradient
```svelte
Rotating gradient
```
### Reactive box-shadow
```svelte
Hover to lift
```
## API Reference
### Signature
```ts
useMotionTemplate(
strings: TemplateStringsArray,
...values: MotionTemplateInput[]
): AugmentedMotionValue
type MotionTemplateInput =
| AugmentedMotionValue
| Readable
```
### Parameters
- **strings** `TemplateStringsArray` — Static template string parts (provided automatically by the tagged template syntax).
- **values** `MotionTemplateInput[]` — Motion values or Svelte readables to interpolate into the template.
### Returns
An `AugmentedMotionValue` — a real motion-dom `MotionValue` containing the composed string, plus:
- `.current` — Svelte 5 reactive getter (templates, `$derived`, `$effect`).
- `.subscribe(run)` — Svelte readable store contract (powers `$filter` template syntax).
- All other `MotionValue` methods from motion-dom (`get`, `on`, etc.).
## When to use
- **CSS filter chains**: Compose `blur()`, `brightness()`, `saturate()` from reactive values
- **Dynamic gradients**: Build `linear-gradient()` or `radial-gradient()` with animated stops
- **Complex box-shadows**: Animate shadow offsets, blur, and spread independently
- **Any CSS value**: Anywhere you need to compose a string from multiple reactive sources
For single-value CSS properties, you can use Svelte's built-in `{$store}` interpolation directly. `useMotionTemplate` is most useful when combining multiple reactive values into one CSS string.
## See also
- [useVelocity](/docs/use-velocity) - Track velocity of store values
- [useTransform](/docs/use-transform) - Map and transform reactive values
- [styleString](/docs/style-string) - Build complete CSS style strings with automatic unit handling
---
Based on [Motion's useMotionTemplate](https://motion.dev/docs/react-use-motion-template) API.
# useMotionValueEvent
> Subscribe to motion value store changes with automatic cleanup.
**Source:** [https://motion.svelte.page/docs/use-motion-value-event](https://motion.svelte.page/docs/use-motion-value-event)
---
`useMotionValueEvent` manages subscriptions to motion value store changes. It skips the initial synchronous emission that Svelte stores produce on subscribe, so the callback only fires on actual *changes*.
```svelte
```
## Usage
### Import
```svelte
```
### Subscribe to changes
Pass a readable store, the event type `'change'`, and a callback:
```svelte
scale.set(2)}
onpointerleave={() => scale.set(1)}
>
Hover me
```
### Available events
| Event | Description |
|-------|-------------|
| `'change'` | Fires whenever the store value updates (after the initial emission). |
### Cleanup
`useMotionValueEvent` returns an unsubscribe function. Call it inside `onDestroy` or at the end of an `$effect`:
```svelte
```
```svelte
```
### Advanced — raw subscribe
For full control you can use `store.subscribe()` directly. Note that Svelte stores fire the callback immediately with the current value on subscribe:
```svelte
```
`useMotionValueEvent` is preferred when you only want to react to *changes* and not the initial value.
## API Reference
### Signature
```ts
useMotionValueEvent(
store: Readable,
event: 'change',
callback: (latest: T) => void
): () => void
```
### Parameters
- **store** `Readable` — any Svelte readable store (e.g. `useSpring`, `useTransform`, `useTime`)
- **event** `'change'` — the event type to listen for
- **callback** `(latest: T) => void` — invoked with the latest value on each change
### Returns
`() => void` — an unsubscribe function that stops the subscription.
## See also
- [Motion values overview](/docs/motion-values) — introduction to motion value stores
- [useSpring](/docs/use-spring) — spring-animated store
- [useTransform](/docs/use-transform) — map and transform stores
- [useVelocity](/docs/use-velocity) — derive velocity from a store
---
Based on [Motion's useMotionValueEvent](https://motion.dev/docs/react-use-motion-value-event) API.
# usePresenceData
> Read AnimatePresence custom data from an exiting child.
**Source:** [https://motion.svelte.page/docs/use-presence-data](https://motion.svelte.page/docs/use-presence-data)
---
`usePresenceData()` returns the nearest ``
value. It matches Motion's hook shape: inside a presence boundary it returns
that boundary's `custom` data, and outside a boundary it returns `undefined`.
The main use case is keyed exit animation. Once a child has been removed from
state, its normal props are stale. Presence data gives that exiting child the
latest parent-level data, such as a carousel direction.
```svelte
{#key selected}
{/key}
```
Inside the keyed child:
```svelte
```
> Live example: [/examples/use-presence-data](https://motion.svelte.page/examples/use-presence-data)
## API Reference
### `usePresenceData(): T | undefined`
Returns the current `custom` value from the nearest ``
boundary.
- Inside ``, returns `value`.
- Outside ``, returns `undefined`.
- In Svelte reactive contexts, wrap it in `$derived(...)` so updates to
`custom` are tracked.
## Related
- [AnimatePresence custom](/docs/animate-presence-custom) — dynamic exit
variants using parent custom data.
- [usePresence](/docs/use-presence) — manually delay removal with
`safeToRemove`.
Based on [Motion's usePresenceData API](https://motion.dev/docs/react-use-presence-data).
# usePresence
> Custom exit animations driven from the child component.
**Source:** [https://motion.svelte.page/docs/use-presence](https://motion.svelte.page/docs/use-presence)
---
`usePresence` and `useIsPresent` let a component branch on whether
`` is keeping it alive for an exit phase. The wrapper
(``) holds the child rendered while `isPresent` is `false`,
and the child runs its own exit animation — CSS transition, canvas effect,
GSAP, anything — and calls `safeToRemove()` when finished.
This is the path to take when the built-in `motion.*` `exit` prop isn't
enough — for example, animating a third-party component, fading text via
CSS classes, or coordinating an exit with non-DOM work.
```svelte
(visible = !visible)}>Toggle
```
Inside ``:
```svelte
…
```
> Live example: [/examples/use-presence](https://motion.svelte.page/examples/use-presence)
## API divergence from React
In framer-motion, `usePresence` works directly inside `` —
React's render tree gives the library control over when children unmount.
Svelte's `{#if}` teardown is synchronous from the user's side and not
interceptable, so you opt-in via the `` wrapper. Bind
`present` to the same condition that would normally gate the children:
```svelte
{visible && }
```
## `useIsPresent`
Returns just the `isPresent` boolean. Useful when you only need to render
different content during exit, no `safeToRemove` needed:
```svelte
…
```
When called outside any `PresenceChild`, `useIsPresent()` returns `true` and
`usePresence()` returns `[true, null]`.
## How `safeToRemove` behaves
- **Idempotent.** Calling it twice is a no-op after the first.
- **Versioned.** Re-entering (`present` flipping back to `true`) before
`safeToRemove` fires cancels the exit; the previously-handed-out callback
becomes a no-op so a stale `transitionend` handler can't tear down a
now-present component.
- **Required.** If you call `usePresence()`, you must eventually call
`safeToRemove`. Otherwise the wrapper holds children forever and
``'s `onExitComplete` never fires.
## Mixing with `motion.*` `exit`
Inside ``, the wrapper drives the exit. Any `motion.*`
descendants automatically opt out of the outer `` clone
path — their `exit` props are ignored. Pick one approach per element:
- Use `motion.*` with `exit={...}` for a declarative motion-driven exit, no
`` needed.
- Use `` with a child that calls `safeToRemove` for a custom
exit you fully control.
## Known limitations
- **`mode='popLayout'`**: the wrapper holds the child in document flow during
exit, so `popLayout` semantics (sibling reflow as the exiting element
leaves layout immediately) are not implemented for ``.
`mode='sync'` (default) and `mode='wait'` work as expected — the wrapper
participates in the same `inFlightExits` accounting as the clone path.
- **Nested `` inside a held ``**: while
the wrapper is holding, descendants don't see exit signals because the
Svelte tree is still mounted. Once you call `safeToRemove`, normal
unmount fires, and any nested motion children's `exit` runs at that point.
## API Reference
### `` props
| Prop | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| `present` | `boolean` | `true` | When this flips `true → false`, the wrapper holds children rendered with `isPresent=false` until `safeToRemove` fires. |
| `children` | `Snippet` | — | Snippet rendered while `present` is true or while the wrapper is holding. |
### `useIsPresent(): boolean`
Returns whether the calling component is currently present. `true` outside
of any ``.
### `usePresence(): [true, null] | [false, () => void]`
Returns the framer-motion-style tuple. `[true, null]` while present (or
outside any ``); `[false, () => void]` once the wrapper
enters its exit hold.
## See also
- [AnimatePresence](/docs/animate-presence) — the parent component.
- [`motion.*` `exit` prop](https://motion.dev/docs/react-motion-component#exit) —
declarative alternative when a built-in transform/style animation is enough.
---
Based on [Motion's usePresence](https://motion.dev/docs/react-use-presence) API.
# useReducedMotionConfig
> Resolve the active reduced-motion policy from and the OS preference.
**Source:** [https://motion.svelte.page/docs/use-reduced-motion-config](https://motion.svelte.page/docs/use-reduced-motion-config)
---
`useReducedMotionConfig` resolves the active reduced-motion policy for the current component subtree by combining the nearest `` ancestor with the OS-level `prefers-reduced-motion` setting.
```svelte
{#if !reduced.current}
{/if}
```
> Live example: [/examples/use-reduced-motion-config](https://motion.svelte.page/examples/use-reduced-motion-config)
## Why two hooks?
- [`useReducedMotion`](/docs/use-reduced-motion) only reads the OS preference.
- `useReducedMotionConfig` reads the resolved policy — it lets a parent
`` override the OS preference (e.g. force-disable motion in a
preview pane regardless of system settings).
## ``
The `reducedMotion` prop on `` controls how transform animations
are handled for descendant `motion` elements:
| Value | Behavior |
| ---------- | ----------------------------------------------------------------------------------------- |
| `'never'` | Default. Animations run as authored. |
| `'always'` | Strip transform keys (`x`, `y`, `scale`, `rotate`, `skew`, …). Other props still animate. |
| `'user'` | Honor the OS-level `prefers-reduced-motion`. Acts like `'always'` when the user opted in. |
```svelte
Fades in always; only translates when the user hasn't opted into reduced motion.
```
## Custom decisions
Use the hook directly when you want to make per-component decisions that go
beyond stripping transforms — for example, swapping a parallax effect for
a static background:
```svelte
{#if reduced.current}
{:else}
{/if}
```
## How it resolves
| Policy | OS pref `reduce` | OS pref `no-preference` |
| ----------- | ---------------- | ----------------------- |
| _no parent_ | `false` | `false` |
| `'never'` | `false` | `false` |
| `'always'` | `true` | `true` |
| `'user'` | `true` | `false` |
## API Reference
### Returns
A `ReducedMotionState` object:
- **`current`** `boolean` (getter) — `true` when descendant motion should be reduced (resolved policy + OS preference). Reactive via `$state` so reads inside templates, `$derived`, and `$effect` track changes to either source automatically.
- **`subscribe(run)`** — Svelte readable store contract. Fires on every OS-preference change AND on every mid-tree `` policy reassignment, so legacy store consumers see both sources. Kept for compat with hooks that still consume Svelte readables.
## See also
- [`useReducedMotion`](/docs/use-reduced-motion) — OS preference only
- [WCAG 2.3.3 Animation from Interactions](https://www.w3.org/WAI/WCAG21/Understanding/animation-from-interactions.html)
---
Based on [Motion's MotionConfig.reducedMotion](https://motion.dev/docs/react-motion-config#reducedmotion) API.
# useReducedMotion
> Reactive Svelte 5 hook for the user's prefers-reduced-motion accessibility preference.
**Source:** [https://motion.svelte.page/docs/use-reduced-motion](https://motion.svelte.page/docs/use-reduced-motion)
---
`useReducedMotion` returns a `$state`-backed `{ current }` object that reflects the user's `prefers-reduced-motion` accessibility setting. `.current` updates live when the media query changes, so components can disable or simplify animations the moment the user toggles the OS preference.
```svelte
Respects the user's preference
```
> Diverges from React framer-motion's plain `boolean | null` return for the same reason as `useCycle`: a `$state`-backed value must live on an object so reads inside getters preserve tracking under Svelte 5 runes.
## Why it matters
Some users disable motion at the OS level because animations cause vestibular discomfort, distraction, or other accessibility issues. `useReducedMotion` gives your components a single source of truth so they can opt out of motion gracefully rather than ignoring the user's setting.
> Live example: [/examples/use-reduced-motion](https://motion.svelte.page/examples/use-reduced-motion)
## Usage
Read `reduced.current` directly in templates, `$derived`, and `$effect` — it's reactive via `$state`:
```svelte
{#if reduced.current}
Animations have been disabled to respect your preference.
{:else}
{/if}
```
### Skipping motion in `motion` components
Combine with `motion` to swap an animated transition for an instant change when reduced motion is requested:
```svelte
```
### With variants
When you build variants, fall back to a "no motion" variant for users who opt out:
```svelte
```
## How it works
- Subscribes to `window.matchMedia('(prefers-reduced-motion: reduce)')` via `$effect`.
- Uses `MediaQueryList` `change` events; falls back to the legacy `addListener` API for Safari < 14.
- Listener is bound to the surrounding reactive scope (the component's lifecycle) — detached automatically on unmount.
- Returns a static `{ current: false }` in SSR or environments without `matchMedia`, so it is safe to call during server rendering.
## API Reference
### Returns
A `ReducedMotionState` object:
- **`current`** `boolean` (getter) — `true` when the user has requested reduced motion, otherwise `false`. Reactive via `$state`.
- **`subscribe(run)`** — Svelte readable store contract. Synchronously emits the current value, then re-emits on every change. Kept for compat with hooks that still consume Svelte readables; prefer `.current` for new code.
## Testing the preference
You don't have to change OS settings to verify your reduced-motion code paths:
- **Chrome / Edge DevTools:** open DevTools → ⋯ → More tools → Rendering →
_Emulate CSS media feature `prefers-reduced-motion`_ → `reduce`.
- **Firefox:** set `ui.prefersReducedMotion` to `1` in `about:config`.
- **Playwright:** `test.use({ reducedMotion: 'reduce' })` or
`await page.emulateMedia({ reducedMotion: 'reduce' })` in a test.
## See also
- [useTime](/docs/use-time) - Drive animations from a reactive time store
- [useAnimationFrame](/docs/use-animation-frame) - Frame-by-frame control
- [WCAG 2.3.3 Animation from Interactions](https://www.w3.org/WAI/WCAG21/Understanding/animation-from-interactions.html)
---
Based on [Motion's useReducedMotion](https://motion.dev/docs/react-use-reduced-motion) API.
# useScroll
> Create scroll-linked animations like progress indicators and parallax effects.
**Source:** [https://motion.svelte.page/docs/use-scroll](https://motion.svelte.page/docs/use-scroll)
---
`useScroll` is used to create scroll-linked animations, like progress indicators and parallax effects.
> **Note:** When scroll-linked animations are powered by the `scroll` function from `motion`, animations using `opacity` or `transform` CSS properties can be hardware accelerated.
```svelte
```
## Usage
### Import
```svelte
```
`useScroll` returns four motion values augmented with a `$state`-backed `.current` getter and a Svelte readable `.subscribe` shim:
| Value | Description |
|-------|-------------|
| `scrollX` | Horizontal scroll position in pixels |
| `scrollY` | Vertical scroll position in pixels |
| `scrollXProgress` | Horizontal scroll progress between `0` and `1` |
| `scrollYProgress` | Vertical scroll progress between `0` and `1` |
Read them with `scrollY.current` in templates and `$derived` / `$effect`, with `scrollY.get()` in imperative code, or with `$scrollY` for store-style consumers. They compose with `useTransform`, `useSpring`, and every other motion-value-aware hook.
### Page scroll
By default, `useScroll` tracks the page scroll position:
```svelte
```
> Live example: [/examples/scroll-progress](https://motion.svelte.page/examples/scroll-progress)
### Element scroll
Track the scroll position of a specific scrollable element by passing it as the `container`:
```svelte
Scroll progress: {scrollYProgress.current}
```
### Element position
Track a target element's position as it scrolls within its container (or the viewport):
```svelte
Fades in as you scroll to this element
```
### Scroll offsets
When tracking an element's position, the `offset` option defines when the tracking starts and ends. Each offset is a pair of intersections — one for the target and one for the container:
```svelte
```
Named offset values:
| Value | Description |
|-------|-------------|
| `"start"` | Top edge of the element |
| `"center"` | Center of the element |
| `"end"` | Bottom edge of the element |
You can also use numbers (`0` to `1`) and pixel values (`"100px"`).
## Performance
Scroll animations work best with CSS properties that can be GPU-accelerated:
- `transform` (translateX, translateY, scale, rotate)
- `opacity`
These properties don't trigger layout or paint, so the browser can animate them on the compositor thread for smooth 60fps performance even during rapid scrolling.
## Options
| Option | Type | Description |
|--------|------|-------------|
| `container` | `HTMLElement` | Scrollable element to track. Defaults to the page. |
| `target` | `HTMLElement` | Target element to track position of within the container. |
| `offset` | `string[]` | Array of scroll offsets defining when tracking starts and ends. |
| `axis` | `'x' \| 'y'` | Which axis to use for the single-axis progress callback. Defaults to `'y'`. |
## API Reference
### Signature
```ts
useScroll(options?: UseScrollOptions): {
scrollX: AugmentedMotionValue
scrollY: AugmentedMotionValue
scrollXProgress: AugmentedMotionValue
scrollYProgress: AugmentedMotionValue
}
```
### Returns
An object with four `AugmentedMotionValue`s — real motion-dom `MotionValue`s with `.current` getter, `.subscribe` shim, and all standard motion-value methods (`get`, `getVelocity`, `on`, etc.).
## See also
- [Motion values overview](/docs/motion-values) — introduction to motion value stores
- [useSpring](/docs/use-spring) — smooth scroll progress with spring physics
- [useTransform](/docs/use-transform) — map scroll progress to visual ranges
- [useMotionValueEvent](/docs/use-motion-value-event) — subscribe to scroll changes
---
Based on [Motion's useScroll](https://motion.dev/docs/react-use-scroll) API.
# useSpring
> Create a spring-animated motion value with physics-based motion.
**Source:** [https://motion.svelte.page/docs/use-spring](https://motion.svelte.page/docs/use-spring)
---
`useSpring` creates a spring-animated motion value. Call `.set(target)` to animate toward a new value with spring physics, or `.jump(value)` to skip the animation.
```svelte
x.set(e.clientX)}
>
Follows pointer with spring physics
```
## Reading the value
The returned object is a real motion-dom `MotionValue` with a Svelte 5 reactive `.current` getter on top. There are three ways to read it:
```svelte
```
`x.current` tracks via `$state`, so reads inside templates, `$derived`, and `$effect` re-run automatically when the spring updates. `$x` is the Svelte 4–style auto-subscribe path — it works because the spring exposes a `.subscribe()` shim, but `.current` is preferred for new code.
## Usage
### From an initial value
Pass a number or unit string to create a spring with that initial value:
```svelte
scale.set(1.2)}
onpointerleave={() => scale.set(1)}
>
Hover to scale
```
### Following another motion value
Pass another `MotionValue` (e.g. from `useMotionValue`, `useScroll`, or another `useSpring`) and the spring will animate toward whatever that source emits:
```svelte
target.set(+e.currentTarget.value)}
/>
Smooth
```
### Following a Svelte readable store
For interop with hooks that still return Svelte stores (`useScroll`, `useTime`), pass the store directly — it's bridged into a motion value internally:
```svelte
```
### With unit strings
`useSpring` preserves unit suffixes like `px`, `deg`, `vh`, `%`:
```svelte
rotation.set('180deg')}>
Flip
```
## Configuration
Customize the spring physics by passing options:
```svelte
```
### Defaults
| Option | Default | Notes |
| ------------- | ------- | ------------------------------------------------------ |
| `stiffness` | `100` | Higher = snappier |
| `damping` | `10` | Higher = less oscillation |
| `mass` | `1` | Higher = more lethargic |
| `restDelta` | `0.001` | Position threshold to settle |
| `restSpeed` | `0.01` | Velocity threshold to settle |
| `velocity` | `0` | Initial velocity |
> **Note:** These defaults match [motion-dom](https://motion.dev/) — and React framer-motion's `useSpring`. They differ from `useSpring`'s prior svelte-motion defaults (`170` / `26`); pass explicit options if you need the older feel.
### Duration-based options
When you'd rather tune by feel than by physics constants, use the duration API:
| Option | Default | Notes |
| ----------------- | ------- | ------------------------------------------------------------------------------------ |
| `duration` | `800`ms | Total animation duration |
| `visualDuration` | — | Visual settle time (overrides `duration` when set); easier to coordinate with tweens |
| `bounce` | `0.3` | `0` = no bounce, `1` = very bouncy |
Setting `stiffness`, `damping`, or `mass` overrides `duration` / `bounce`.
### `skipInitialAnimation`
When following a source motion value, the spring normally animates from its initial value to whatever the source emits first. For scroll-restoration or back-navigation scenarios where the first emit is the "current" position rather than a target, set `skipInitialAnimation: true` so the spring jumps to the first value and only animates on subsequent updates:
```svelte
```
## Methods
### `set(value)`
Animate toward a new target value:
```svelte
```
If the spring is mid-animation, the existing velocity carries over into the new target — no visible discontinuity.
### `jump(value)`
Immediately set the value without animation:
```svelte
```
Useful for resetting state or initializing to a known position.
### `get()`
Read the current value imperatively:
```svelte
```
Prefer `x.current` inside reactive scopes (templates, `$derived`, `$effect`); use `.get()` in event handlers and other one-shot reads.
### `on(event, callback)`
Subscribe to motion value events. Returns an unsubscribe function.
```svelte
```
### `destroy()`
Tear down the spring early. Normally not needed — the spring auto-cleans up when its surrounding component unmounts.
## How it works
`useSpring` returns a `MotionValue` from `motion-dom` (the same primitive used by every other motion value in this library). The spring physics are computed by motion-dom's `attachFollow` + `JSAnimation` — the same engine React framer-motion uses, so behavior, defaults, and option semantics are 1:1.
The Svelte 5 layer adds:
- A `.current` getter backed by `$state`, kept in sync with the motion value's `change` event so reads inside reactive scopes track automatically.
- A `.subscribe(run)` shim implementing the Svelte readable store contract, so legacy `$store` syntax and store-consumers (`useTransform` function form, `useVelocity`, `derived(...)`, etc.) keep working.
- Lifecycle binding via `$effect`, so the spring auto-cleans when the component unmounts.
## Performance
- **On-demand:** The animation loop only runs while the spring is in motion.
- **Auto-settle:** Stops computing once both position delta and velocity fall below `restDelta` / `restSpeed`.
- **Velocity handoff:** Mid-animation retargets carry velocity into the new spring, so rapid input doesn't produce visual jumps.
- **SSR-safe:** Returns a static motion value with no-op `.set` / `.jump` on the server.
## Common patterns
### Pointer tracking
```svelte
{ x.set(e.clientX); y.set(e.clientY) }}
/>
Cursor follower
```
### Toggle animation
```svelte
{ toggled = !toggled; rotation.set(toggled ? 180 : 0) }}>
Toggle
```
### With useVelocity
Track the velocity of a spring for momentum-based effects:
```svelte
Momentum skew
```
> `useVelocity` and `useTransform` still return Svelte stores in this release (`$skew`). They'll migrate to motion values in a future release; the `.current` pattern will apply there too.
## API Reference
### Signature
```ts
useSpring(
source: number | string | MotionValue | Readable,
options?: UseSpringOptions
): SpringMotionValue
```
### Parameters
- **source** — Initial value, unit string, another `MotionValue` to follow, or a Svelte readable store to follow.
- **options** — `SpringOptions` plus `skipInitialAnimation`. See [Configuration](#configuration).
### Returns
A `SpringMotionValue` — a real motion-dom `MotionValue` augmented with:
- **`current`** `T` (getter) — Svelte 5 reactive read backed by `$state`.
- **`set(value)`** — animate toward a new target.
- **`jump(value)`** — set immediately, no animation.
- **`get()`** — imperative current-value read.
- **`on(event, cb)`** — subscribe to `'change'` / `'animationStart'` / `'animationComplete'` / `'animationCancel'` / `'destroy'`.
- **`subscribe(run)`** — Svelte readable store contract for `$store` syntax and store-consumers.
- **`destroy()`** — early teardown.
- All other `MotionValue` methods from motion-dom (`getVelocity`, etc.).
## When to use
- **Smooth value transitions** — animate any numeric value with natural-feeling motion.
- **Pointer following** — track cursor or touch position with spring physics.
- **Interactive toggles** — smoothly animate between states on user interaction.
- **Smoothing scroll progress** — wrap `useScroll`'s output for buttery progress bars and parallax.
For gesture-driven springs, combine with event handlers. For time-based animations, see [useTime](/docs/use-time) + [useTransform](/docs/use-transform).
## See also
- [useVelocity](/docs/use-velocity) — track the velocity of a spring or any motion value.
- [useTransform](/docs/use-transform) — map spring values to other ranges.
- [useMotionTemplate](/docs/use-motion-template) — compose CSS strings from spring values.
- [useTime](/docs/use-time) — reactive time source for continuous animations.
---
Based on [Motion's useSpring](https://motion.dev/docs/react-use-spring) API; physics delegated to motion-dom's `attachFollow`.
# useTime
> A motion value that ticks once per animation frame with elapsed milliseconds.
**Source:** [https://motion.svelte.page/docs/use-time](https://motion.svelte.page/docs/use-time)
---
`useTime` returns a motion-dom `MotionValue` that updates once per animation frame with elapsed milliseconds since the value was created. It's a real motion value augmented with a `$state`-backed `.current` getter and a Svelte readable `.subscribe` shim — so it composes with `useTransform`, `useSpring`, and the rest of the Tier 2 surface.
```svelte
Rotating content
```
## Usage
The store value represents elapsed milliseconds, making it perfect for time-based animations that need to stay in sync with Svelte's reactivity system.
```svelte
Animated content
```
> Live example: [/examples/use-time](https://motion.svelte.page/examples/use-time)
## Shared timelines
Pass an `id` string to share the same RAF loop **across multiple components**. Each call returns its **own** motion value — destroying one consumer's value doesn't ripple to others — but they all observe the same underlying timeline, so the values stay perfectly in lockstep. The shared loop runs while at least one consumer is alive and stops the moment the last one unmounts; the next `useTime(id)` call restarts it.
> **Note**: Within a single component, you can simply reuse the same motion-value reference. The `id` parameter is specifically useful for synchronizing animations across different components that don't share scope.
```svelte
Synced animation
```
### Multiple components synchronized
```svelte
Time: {$time}ms
Same time: {$time}ms
```
Even though these are **separate components**, they both receive updates from the same timeline by using the same `id`, ensuring perfect synchronization.
### Synced timeline example
Here's a complete example demonstrating the power of shared timelines. Notice how two **separate** `useTime()` calls with the same `id` return independent-but-synchronized motion values — different references, identical values every frame:
```svelte
A
B
time: {$time}ms = time2: {$time2}ms
```
> Live example: [/examples/use-time-synced](https://motion.svelte.page/examples/use-time-synced)
Both elements animate in perfect synchronization even though they use **different motion-value references** (`time` and `time2`). The magic happens because both `useTime('synced-timeline')` calls observe the same underlying timeline — independent values, shared frame loop. Watch the values display - they're always identical! This is especially powerful when these elements live in **separate components** - the `id` parameter ensures they all stay in sync without prop drilling or context.
## How it works
`useTime` wraps motion-dom's `MotionValue` with a per-frame RAF loop:
1. Creates a `MotionValue` that writes elapsed milliseconds on every `requestAnimationFrame`
2. With an `id`, multiple consumers observe the **same** RAF loop — each call still returns an independent motion value (so destroying one doesn't affect others) but they stay in lockstep
3. The RAF loop attaches at component mount via `$effect` and detaches at unmount — when the last consumer of a shared `id` unmounts, the loop stops; the next `useTime(id)` call restarts it
### Read patterns
```svelte
```
The RAF loop starts at component mount and stops at unmount — no subscriber tracking required.
## Performance
`useTime` is optimized for smooth animations:
- **Frame-perfect**: Updates at display refresh rate (typically 60 FPS)
- **Efficient**: Single `requestAnimationFrame` loop per unique timeline
- **Shared resources**: Multiple consumers of the same `id` share one loop
- **Auto-cleanup**: Cancels the frame loop on component unmount
## Common patterns
### Smooth rotation
```svelte
↻
```
### Oscillating scale
```svelte
Pulsing
```
### Color cycling
```svelte
Rainbow
```
### Multiple synchronized animations
```svelte
Complex animation
```
## API Reference
### Parameters
- **id** `string` (optional) - Timeline identifier for sharing across components
### Returns
An `AugmentedMotionValue` — a real motion-dom `MotionValue` containing elapsed milliseconds since creation, plus:
- `.current` — Svelte 5 reactive getter (templates, `$derived`, `$effect`).
- `.subscribe(run)` — Svelte readable store contract (powers `$time` template syntax and `derived(time, …)`).
- `.get()` / `.on('change', cb)` / all other motion-value methods from motion-dom.
The motion value's lifecycle is bound to the surrounding `$effect`: the RAF loop starts at mount and stops at unmount. For shared `id` timelines the loop stops only when the last consumer unmounts.
## When to use
Use `useTime` when you need:
- **Reactive time values** - Integrate time into Svelte's reactive system
- **Store-based animations** - Derive multiple animated values from one timeline
- **Cross-component synchronization** - Keep separate components in sync with shared timeline `id`
- **Declarative time** - Use `$time` syntax for clean, reactive code
For direct DOM manipulation or frame-by-frame control, consider [useAnimationFrame](/docs/use-animation-frame) instead.
## Comparison with useAnimationFrame
| Feature | useTime | useAnimationFrame |
|---------|---------|-------------------|
| Returns | Reactive store | Cleanup function |
| Usage | Declarative with `$` | Imperative callback |
| Integration | Svelte stores | Direct DOM |
| Derived values | Easy with `derived()` | Manual calculation |
| Synchronization | Built-in with `id` | Manual coordination |
## See also
- [useAnimationFrame](/docs/use-animation-frame) - For direct frame control
- [useTransform](/docs/use-transform) - For mapping and transforming values
- [styleString](/docs/style-string) - For building CSS style strings with automatic unit handling
- [Examples](/examples) - See useTime in action
---
Based on [Motion's useTime](https://motion.dev/docs/react-use-time) API.
# useTransform
> Create a MotionValue that maps another motion value across input/output ranges or computes from dependencies.
**Source:** [https://motion.svelte.page/docs/use-transform](https://motion.svelte.page/docs/use-transform)
---
`useTransform` creates a `MotionValue` derived from another motion value (or any Svelte readable). It supports two forms:
- Mapping form: Map a numeric source across input/output ranges with options like `clamp`, `ease`, and `mixer`.
- Compute form: Recompute from a function whose `MotionValue` reads are auto-tracked. Plus single-MV and multi-MV transformer forms, and a multi-output mapping form.
The returned value is a real motion-dom `MotionValue` augmented with a `$state`-backed `.current` getter and a Svelte readable `.subscribe` shim — read it via `transformed.current` in templates, `$transformed` for store-style consumers, or `transformed.get()` in imperative code.
```svelte
Rotating
```
> Live example: [/examples/html-content](https://motion.svelte.page/examples/html-content)
## Usage
### Mapping form
Map a numeric source across input/output ranges. You can shape interpolation with `ease`, clamp input to segment bounds with `clamp`, and provide a custom `mixer` for non-numeric outputs.
```svelte
↻
```
#### With easing
Provide a single easing or one per segment.
```svelte
Pulsing
```
#### Non-numeric outputs with mixer
For non-numeric outputs, pass a `mixer(from, to)` that returns an interpolator `(t) => value`.
```svelte
```
### Compute form (auto-tracking)
Pass a compute function with no deps array. Every `MotionValue` whose `.get()` (or `.current`) is read inside the function is automatically tracked — motion-dom's `collectMotionValues` discovers them during the initial seed call.
```svelte
Total: {total.current}
```
For mixed `MotionValue` + Svelte readable scenarios, sample the readable via `get(readable)` inside the compute (or `$store` syntax). Readables don't participate in `collectMotionValues`, so their values are sampled when an adjacent motion value triggers a recompute.
### Single-MV / multi-MV transformer forms
For straightforward 1→1 or N→1 transforms, the transformer-style overloads are terser than the compute form:
```svelte
```
### Multi-output mapping form
Map a single source to many output ranges in one call. Returns an object of motion values keyed by your map:
```svelte
…
```
## How it works
- Mapping form picks the active input segment and interpolates between its corresponding outputs.
- `clamp` (default `true`) limits the input to current segment bounds; set `false` to allow extrapolation.
- `ease` shapes the 0..1 progress before mixing.
- If outputs are numeric, a linear mixer is used; otherwise provide a custom `mixer`.
- Descending input ranges are supported. Equal segment endpoints produce zero progress for that segment.
## API Reference
### Signatures
```ts
// Mapping form
useTransform(source, input, output, options?)
source: MotionValue | Readable // numeric source
input: number[] // input stops
output: T[] // output stops (same length as input)
options.clamp: boolean // clamp to active segment (default true)
options.ease: Function | Function[] // easing per segment
options.mixer: (from, to) => (t) => value // custom mixer
Returns: AugmentedMotionValue
// Single-MV transformer form
useTransform(mv, (latest) => out)
mv: MotionValue // source motion value
transformer: (latest: I) => O // map latest into output
Returns: AugmentedMotionValue
// Multi-MV transformer form
useTransform([mv1, mv2, …], ([a, b, …]) => out)
sources: Array // source motion values
transformer: (latest: I[]) => O // combine latest values
Returns: AugmentedMotionValue
// Multi-output mapping form
useTransform(source, input, outputMap, options?)
source: MotionValue | Readable
input: number[]
outputMap: { [key]: T[] } // one output range per key
Returns: { [key]: AugmentedMotionValue }
// Compute form (auto-tracking — no deps array)
useTransform(() => compute)
compute: () => T // reads .get() on any MotionValues
Returns: AugmentedMotionValue
// Inside compute, call mv.get() (or read mv.current) on each MotionValue
// you want tracked. motion-dom's collectMotionValues discovers them
// automatically during the seed call — no explicit deps array.
```
### Parameters
- `source` `MotionValue | Readable`: Numeric source (mapping form).
- `input` `number[]`: Input stops (length must match `output`).
- `output` `T[]`: Output stops (same length as `input`).
- `outputMap` `{ [key: string]: T[] }`: Object of output ranges, one per key. Returns an object of motion values with the same keys.
- `options.clamp` `boolean` (default `true`): Clamp to active segment.
- `options.ease` `((t: number) => number) | Array<...>`: Easing per segment or single easing.
- `options.mixer` `(from, to) => (t) => any`: Custom mixer for non-numeric outputs.
- `transformer` `(latest) => O` / `([latest, …]) => O`: Transform / combine function (single-MV / multi-MV forms).
- `compute` `() => T`: Compute function (compute form). MotionValues read via `.get()` inside are auto-tracked.
### Returns
An `AugmentedMotionValue` — a real motion-dom `MotionValue` (so it composes with `useTransform`, `useSpring`, `animate()`, etc.) plus:
- `.current` — Svelte 5 reactive getter for templates / `$derived` / `$effect`.
- `.subscribe(run)` — Svelte readable store contract (powers `$transformed` template syntax).
- All other `MotionValue` methods from motion-dom (`get`, `getVelocity`, `on`, etc.).
## When to use
- Link styles directly to time or gesture progress.
- Derive values from other stores using a declarative, reactive API.
- Map ranges with easing and clamp behavior without manual math.
- Interpolate non-numeric outputs via a custom mixer.
## See also
- [useTime](/docs/use-time) — Time source for mapping and progress.
- [useAnimationFrame](/docs/use-animation-frame) — Imperative frame callback.
- [styleString](/docs/style-string) — Build CSS style strings with automatic unit handling.
---
Based on [Motion's useTransform](https://motion.dev/docs/react-use-transform?platform=react) API.
# useVelocity
> Track the velocity of a motion value in units per second.
**Source:** [https://motion.svelte.page/docs/use-velocity](https://motion.svelte.page/docs/use-velocity)
---
`useVelocity` returns an augmented `MotionValue` tracking the velocity of a source's value in units per second. Pass a motion value (from `useMotionValue`, `useSpring`, `useScroll`, …) or any Svelte readable; the result composes with `useTransform`, `useSpring`, and the rest of the Tier 2 surface.
```svelte
Velocity: {xVelocity.current} px/s
```
## Usage
The returned value emits the rate of change in units per second and settles to `0` when the source stops moving.
```svelte
x.set(e.clientX)}
>
Move your pointer
```
### With unit strings
`useVelocity` parses numeric values from unit strings like `"120px"` or `"45deg"`:
```svelte
```
## How it works
1. Creates an internal `motionValue` from `motion-dom` for timestamp-based velocity tracking
2. Subscribes to the source and forwards each emit to the tracker via `.set()`
3. Polls `motionValue.getVelocity()` on every `requestAnimationFrame` and writes the result to the returned motion value
4. Snaps the result to `0` and stops polling when velocity falls below `0.001` units/second; the next source emit restarts the loop
5. The source subscription and both internal motion values are torn down when the surrounding `$effect` scope unmounts
## Performance
- **On-demand polling**: The RAF loop only runs while velocity is non-zero
- **Auto-settle**: Stops polling when movement stops, avoiding idle CPU usage
- **Cleanup**: Cancels the frame loop and unsubscribes the source at component unmount
- **SSR-safe**: Returns a static `motionValue(0)` with no source subscription on the server
## Common patterns
### Momentum-based skew
```svelte
x.set(e.clientX)}
>
Skews with momentum
```
### Velocity-driven blur
```svelte
Blurs when moving fast
```
### Chaining velocities
You can derive the velocity of a velocity to get acceleration:
```svelte
```
## API Reference
### Parameters
- **source** `MotionValue | Readable` — a motion value or Svelte readable whose value changes over time.
### Returns
An `AugmentedMotionValue` — a real motion-dom `MotionValue` containing the current velocity in units per second, plus:
- `.current` — Svelte 5 reactive getter (templates, `$derived`, `$effect`).
- `.subscribe(run)` — Svelte readable store contract (powers `$velocity` template syntax).
- All other `MotionValue` methods from motion-dom (`get`, `getVelocity`, `on`, etc.).
## When to use
- **Momentum effects**: Skew, stretch, or blur elements based on how fast a value is changing
- **Physics-based UI**: React to acceleration or deceleration of animations
- **Gesture feedback**: Show speed-dependent visual feedback during pointer or scroll interactions
- **Chained dynamics**: Derive acceleration by passing a velocity store back into `useVelocity`
## See also
- [useTransform](/docs/use-transform) - Map velocity values to visual ranges
- [useMotionTemplate](/docs/use-motion-template) - Compose CSS strings from reactive stores
- [useTime](/docs/use-time) - Reactive time source for continuous animations
---
Based on [Motion's useVelocity](https://motion.dev/docs/react-use-velocity) API.
# useWillChange
> Auto-managed CSS will-change that starts at auto and latches to transform after a qualifying animation.
**Source:** [https://motion.svelte.page/docs/use-will-change](https://motion.svelte.page/docs/use-will-change)
---
`useWillChange()` returns a MotionValue you assign to an element's
`style.willChange`. It manages its own value: it stays `auto` until a transform
or accelerated property animates on that element, then flips to `transform`.
Setting `will-change` permanently forces the browser to keep an element on its
own compositor layer — which costs memory. `useWillChange` starts at `auto` and
flips to `transform` when a transform or accelerated property animates, then
stays latched — avoiding eager promotion before first use while still capturing
the compositor win once it's needed. Mirrors framer-motion's `useWillChange`.
```svelte
```
> Live example: [/examples/use-will-change](https://motion.svelte.page/examples/use-will-change)
## How it works
Assign the value through object-form `style`. As animations start, the motion
runtime notifies the value which keys are animating:
- A transform shortcut (`x`, `y`, `scale`, `rotate`, …) or an accelerated value
(e.g. `opacity`) flips `will-change` to `transform`.
- Any other property (e.g. `backgroundColor`) leaves it at `auto`.
Once flipped, the hint stays `transform` — there's no benefit to thrashing it
back to `auto` between animations.
## API Reference
### `useWillChange`
```ts
function useWillChange(): WillChangeMotionValue
```
Returns a `MotionValue` (so it composes with object-form `style`) with
an extra `add(name)` method the runtime calls as animations start. Assign it to
`style.willChange`.
Based on [Motion's useWillChange hook](https://motion.dev/docs/react-use-will-change).
# Vanilla motion values
> Component-free motion values — motionValue, springValue, transformValue, mapValue, styleEffect and the toMotionValue rune bridge
**Source:** [https://motion.svelte.page/docs/vanilla-values](https://motion.svelte.page/docs/vanilla-values)
---
Everything the `use*` motion-value hooks do, **without a component**: create values in module scope, `.svelte.ts` stores, or event handlers, and bind them straight to plain DOM elements with `styleEffect` — mirroring Motion's "vanilla JavaScript" API.
```ts
// stores/scroll.svelte.ts — impossible with useMotionValue
import { motionValue } from '@humanspeak/svelte-motion'
export const scrollProgress = motionValue(0)
```
## Hooks vs vanilla
Both layers return the **same augmented value**: a real motion-dom `MotionValue` (passes `isMotionValue`, works with `animate()`) with a rune-reactive `.current` and a store `.subscribe`. The only difference is lifecycle:
| | `use*` hooks | Vanilla factories |
|---|---|---|
| Where they work | Component init only | Anywhere |
| Cleanup | Automatic on unmount | Manual `.destroy()` |
| Examples | `useMotionValue`, `useSpring`, `useTransform` | `motionValue`, `springValue`, `transformValue`, `mapValue` |
Values that live for the app's lifetime (module-scope stores) never need destroying. Values created per-interaction should be destroyed when done — a getter-bridged value holds a live Svelte effect until then.
## The factories
```ts
import { mapValue, motionValue, springValue, transformValue } from '@humanspeak/svelte-motion'
const x = motionValue(0)
// Range mapping — numbers, colors, unit strings
const opacity = mapValue(x, [0, 200], [1, 0])
const color = mapValue(x, [0, 200], ['#ff0088', '#00ccff'])
// Springs toward a source (values or unit strings)
const smooth = springValue(x, { stiffness: 300, damping: 30 })
// Computed from .get() reads — dependencies auto-tracked
const label = transformValue(() => `${Math.round(x.get())}px`)
```
`springValue` and `mapValue` accept any source kind: a `MotionValue`, a Svelte readable store, or a reactive getter.
## `toMotionValue` — the rune bridge
Turn **any** Svelte state into a motion value. The getter form tracks `$state` / `$derived` reads (and other augmented values' `.current`):
```svelte
Fades with the slider
```
Passing an existing `MotionValue` returns the same value; passing a readable store returns a mirroring value. Getter-driven updates flush on Svelte's effect schedule (a microtask), matching template timing.
The hooks understand getters too — `useSpring(() => target)` and `useTransform(() => count, [0, 10], [0, 100])` track rune state the same way.
## Element effects
Bind values directly to elements — no motion component in sight:
```ts
import { attrEffect, styleEffect, svgEffect } from '@humanspeak/svelte-motion'
const stop = styleEffect('.box', { x, opacity }) // inline styles
attrEffect(input, { value: count }) // attributes
svgEffect(path, { pathLength: progress }) // SVG attributes
```
Each returns an unbind function. Import them from this package (not `motion`) so both raw and augmented values type-check.
## Scheduler, generators, config
The rest of Motion's vanilla toolkit re-exports directly:
```ts
import {
cancelFrame,
frame, // frameloop scheduler: frame.read / frame.update / frame.render
MotionGlobalConfig, // e.g. skipAnimations for tests
scrollInfo,
spring // spring generator for WAAPI / custom drivers
} from '@humanspeak/svelte-motion'
frame.render(() => {
// runs on Motion's render step this frame
})
```
Plus the `steps`, `mirrorEasing`, and `reverseEasing` easing helpers alongside the existing easing exports.
## Related
- [Vanilla Values example](/examples/vanilla-values) — this page's demo with full source
- [Motion values overview](/docs/motion-values) — the hook layer and the augmented value shape
- [useTransform](/docs/use-transform) / [useSpring](/docs/use-spring) — component-scoped counterparts
- [Motion component](/docs) — object-form `style` accepts these values directly
---
Based on [Motion's vanilla motion value](https://motion.dev/docs/motion-value) API.
# Variants
> Define reusable animation states and orchestrate complex animations across component trees
**Source:** [https://motion.svelte.page/docs/variants](https://motion.svelte.page/docs/variants)
---
Variants allow you to define named animation states that can be referenced throughout your component tree. They're perfect for creating reusable animations and orchestrating complex sequences.
## Basic usage
Instead of defining animation objects inline, you can create a `Variants` object with named states:
```svelte
isOpen = !isOpen}
>
Click me
```
> Live example: [/examples/variants-basic](https://motion.svelte.page/examples/variants-basic)
## Benefits
### 1. Reusable animation definitions
Define your animation states once and reference them by name throughout your components:
```svelte
```
### 2. Clean state management
Variants work beautifully with Svelte's reactive state:
```svelte
{status}
```
### 3. Simplified animation orchestration
Variants make it easy to coordinate animations across multiple elements without prop drilling.
## Variant propagation
One of the most powerful features of variants is **automatic propagation** through component trees. When a parent component changes its animation state, all children with matching variant names will animate automatically.
```svelte
Item 1
Item 2
Item 3
```
> Live example: [/examples/variants-propagation](https://motion.svelte.page/examples/variants-propagation)
### How propagation works
1. Parent component sets `animate="visible"`
2. Children with `variants` defined automatically inherit `"visible"`
3. Each child resolves its own `visible` variant from its local variants definition
4. No need to pass `animate` props to children!
## Stagger animations
You can create staggered animations by using the `delay` in individual item transitions:
```svelte
{#each items as item, i}
{item}
{/each}
```
## Complex example: Notifications Stack
Here's a real-world example showing how variants enable complex orchestration. The parent controls the animation state, and all children respond accordingly.
**Click the stack to see variants in action:**
> Live example: [/examples/notifications-stack](https://motion.svelte.page/examples/notifications-stack)
### Breaking down the code
The example uses three different variant definitions working together:
```svelte
Notifications
isOpen = false}>Collapse
{#each notifications as notification, i}
isOpen = !isOpen}
>
{notification}
{/each}
```
**Key points:**
- Parent sets `animate={isOpen ? 'open' : 'closed'}`
- Children with `variants` automatically inherit this state
- Each child resolves its own variant definition
- No need to pass props to children!
See the [full source code](/examples/notifications-stack) for the complete implementation.
## Type safety
Variants are fully typed in TypeScript. The `Variants` type ensures your animation definitions are valid:
```typescript
import type { Variants } from '@humanspeak/svelte-motion'
const variants: Variants = {
visible: {
opacity: 1,
x: 0
},
hidden: {
opacity: 0,
x: -100
}
}
```
## Best practices
### 1. Use semantic names
Choose variant names that describe the state, not the animation:
```svelte
// ✅ Good - describes state
const variants = {
visible: { opacity: 1 },
hidden: { opacity: 0 }
}
// ❌ Avoid - describes animation
const variants = {
fadeIn: { opacity: 1 },
fadeOut: { opacity: 0 }
}
```
### 2. Keep variants focused
Each variant should represent a complete state:
```svelte
const buttonVariants: Variants = {
idle: { scale: 1, backgroundColor: 'gray' },
loading: { scale: 1.05, backgroundColor: 'royalblue' },
success: { scale: 1, backgroundColor: '#22c55e' },
error: { scale: 0.95, backgroundColor: '#ef4444' }
}
```
### 3. Combine with Svelte state
Variants work perfectly with Svelte's reactive state management:
```svelte
```
## API Reference
### Variants type
```typescript
type Variants = Record
```
A `Variants` object is a dictionary mapping variant names (strings) to animation definitions.
### Using variants
Variants can be passed to any motion component via the `variants` prop:
- `variants`: Object containing named animation states
- `initial`: Initial variant name (string or `string[]`)
- `animate`: Target variant name (string or `string[]`)
- `exit`: Exit variant name (string or `string[]`)
- `whileHover` / `whileTap` / `whileFocus` / `whileDrag` / `whileInView`: variant name (string or `string[]`)
```svelte
```
### Variant keys on gesture props
Pass a variant name to any `whileX` prop to reuse a named state across gestures — handy when the same animation target shows up in multiple places (a single `hover` variant can drive `whileHover` on every card in a list, for example).
```svelte
hover or tap
```
You can also pass an **array of variant keys** to combine multiple states. The keys merge left-to-right — later entries override earlier ones on key collisions.
```svelte
```
If a key is missing from `variants`, it's silently skipped — useful for conditional layering.
## Related
- [Motion Component](/docs) - Full API reference
- [Examples](/examples) - See variants in action
# View Transitions
> Animate between two DOM states with the browser View Transitions API — crossfades, shared-element morphs, and enter/exit animations via animateView
**Source:** [https://motion.svelte.page/docs/view](https://motion.svelte.page/docs/view)
---
`animateView` animates between two DOM states using the browser's native [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API) — whole-page crossfades, shared-element morphs (a thumbnail flying into a hero), and enter/exit animations for appearing and disappearing elements, all driven by Motion's spring-capable timing.
```svelte
```
## How it works
`animateView(update, options?)` takes an **update callback** that performs the state change producing the new view. The browser snapshots the page before and after the update, then animates between the snapshots.
Svelte `$state` mutations made inside `update` are flushed to the DOM synchronously before the new snapshot is captured — plain assignment just works, no `tick()` ceremony:
```ts
animateView(() => (showDetail = true))
```
The returned builder is **thenable** — `await` it for completion:
```ts
await animateView(() => (items = shuffle(items))).add('[data-card]')
console.log('transition finished')
```
## Opting the page in
A bare call with no chained subject swaps **instantly by design** — nothing is captured unless something opts in. Chain `.layout()` on the implicit root subject for a whole-page crossfade:
```ts
animateView(() => (theme = 'dark'), { duration: 0.5 }).layout()
```
## Shared-element morphs
`.add(oldTarget, newTarget)` pairs two different elements as one layer: the first resolves in the **old** snapshot, the second in the **new** one, and the browser morphs between them — the "now playing" pattern:
```ts
// Thumbnail grows into the detail hero…
animateView(() => (selected = album)).add(`[data-thumb="${album.id}"]`, '[data-hero]')
// …and morphs back on close.
animateView(() => (selected = null)).add('[data-hero]', `[data-thumb="${album.id}"]`)
```
Targets can be CSS selectors or `Element`s. `view-transition-name`s are generated, applied, and cleaned up automatically.
With a single argument, `.add(target)` registers every matched element — survivors morph to their new positions, and pure newcomers/leavers can animate via `.enter()` / `.exit()`:
```ts
animateView(() => (filter = 'circles'))
.add('[data-item]')
.enter({ opacity: [0, 1], scale: [0.6, 1] })
.exit({ opacity: [1, 0], scale: [1, 0.6] })
```
`.new()` and `.old()` are the ungated variants — they animate the new/old view of a layer whenever it exists, including survivors, for crossfades and slide-throughs.
## Matching corner radii across morph endpoints
View-transition snapshots are paints of the live DOM, so an element's corner rounding is **baked into its snapshot as transparency**. If the two ends of a shared-element morph have different *proportional* rounding, the outgoing snapshot's corners ghost through during the crossfade.
Give both endpoints the **same percentage radius** so the silhouettes coincide at every scale:
```css
.thumb,
.hero {
border-radius: 12%; /* not 14px on one and 20px on the other */
}
```
For morphs whose **aspect ratio changes**, this is handled automatically — the layer is cropped with `object-fit: cover` and its clip radius animates from the old radius to the new. `.crop(true)` / `.crop(false)` overrides the automatic behavior.
## Options
The second argument sets default transition options for every layer (a subject's own `.layout()` / `.enter()` / … options win), plus `interrupt`:
```ts
animateView(update, {
type: 'spring',
visualDuration: 0.4,
bounce: 0.2,
interrupt: 'immediate' // skip an in-flight transition; 'wait' (default) queues
})
```
## Browser support
Browsers without `document.startViewTransition` still run the update — the view swaps instantly and the returned promise resolves, so no feature-gating is needed at call sites. As of 2026 the API is supported in Chromium and Safari 18+; Firefox support is in development.
## Builder API
| Method | Description |
|--------|-------------|
| `.add(target, newTarget?)` | Register elements by selector/Element; a second target pairs two elements into one shared-element morph |
| `.layout(options?)` | Customize the morph timing; on the root subject, opts the page into the crossfade |
| `.enter(keyframes, options?)` | Animate a pure newcomer's new view |
| `.exit(keyframes, options?)` | Animate a pure leaver's old view |
| `.new(keyframes, options?)` | Animate the new view of any layer, including survivors |
| `.old(keyframes, options?)` | Animate the old view of any layer, including survivors |
| `.crop(enabled?)` | Force the clip + animated corner radii on or off |
| `.class(name)` | Tag the layer with a `view-transition-class` for CSS targeting |
| `.group(enabled?)` | Opt out of DOM-hierarchy layer nesting so the layer escapes an ancestor's clip |
| `await builder` | Resolves when the transition completes |
## Related
- [Layout Animations](/docs/layout-animations) — FLIP-based morphs for elements that persist in the DOM
- [AnimatePresence](/docs/animate-presence) — component-level exit animations
- [Motion values](/docs/motion-values) — drive styles imperatively
---
Based on [Motion's animateView](https://motion.dev/docs/view) API.