---
title: Animate
description: Animate elements with Motion's animation props, using the mercury attachment.
---

## Overview

`mercury()` is Mercury's equivalent of a `<motion.div>`. It is a Svelte [attachment](https://svelte.dev/docs/svelte/@attach) that takes the props of a Motion component — `initial`, `animate`, `exit`, `transition`, `variants`, the `while*` gestures, `drag` and the rest — and applies them to the element it's on:

```svelte
<script lang="ts">
	import { mercury } from '@omicrxn/mercury';
</script>

<div {@attach mercury({ animate: { scale: 1.5, x: 50 } })} />
```

Under the hood each element gets a Motion `VisualElement` and Motion's own animation state machine, the same engine `motion/react` runs on. Gesture states, variants and their propagation to children, `transitionEnd`, reduced motion and value velocity all behave as they do in Motion.

## State-driven animation

Attachments re-run when the state they read changes. Each run updates the same Motion node — like a React re-render of a motion component — so derive `animate` from `$state` and Mercury animates whatever changed:

```svelte
<script lang="ts">
	let isSilent = $state(false);
</script>

<div
	{@attach mercury({
		animate: { width: isSilent ? 148 : 128 },
		transition: { type: 'spring', bounce: 0.5 }
	})}
/>
```

Values that didn't change don't replay, gestures in progress carry on, and an interrupted animation continues from its current value and velocity.

## initial

`initial` is the state the element renders before it animates to `animate` on mount:

```svelte
<div
	{@attach mercury({
		initial: { opacity: 0, y: 16 },
		animate: { opacity: 1, y: 0 }
	})}
/>
```

Set `initial: false` to render the element directly in its `animate` state, so only later changes animate (keyframe arrays start at their last frame):

```svelte
<!-- no wiggle on mount; wiggles on every isSilent change -->
<div
	{@attach mercury({
		initial: false,
		animate: {
			rotate: isSilent ? [0, -15, 5, -2, 0] : [0, 20, -15, 12.5, -10, 10, -7.5, 7.5, -5, 5, 0]
		}
	})}
/>
```

Without `initial`, the element animates from its current styles.

:::note[Server-rendered pages]
Mercury applies `initial` before the browser paints an element that Svelte adds. Markup that came from the server is painted before Svelte hydrates it, so it shows its CSS styles first. For a flash-free entrance on server-rendered content, give the element its hidden state in CSS as well (`class="opacity-0"`), or animate it in with [`presence`](/docs/api/presence).
:::

## exit

`exit` is the state the element animates to before it's removed. Like Motion's `exit`, which needs `<AnimatePresence>`, it needs [`presence`](/docs/api/presence) on the element so Svelte keeps it in the page until the exit has played:

```svelte
{#if show}
	<div
		transition:presence
		{@attach mercury({
			initial: { opacity: 0, scale: 0.9 },
			animate: { opacity: 1, scale: 1 },
			exit: { opacity: 0, scale: 0.9 }
		})}
	/>
{/if}
```

## Keyframes

Arrays animate through each value in turn. `null` as the first keyframe means "the current value":

```svelte
<div {@attach mercury({ animate: { y: [64, 0], scale: [null, 1.2, 1] } })} />
```

Position keyframes with `times` (one 0–1 value per keyframe) in the transition.

## Transition

`transition` takes Motion's [transition options](https://motion.dev/docs/animate#transitions) — `duration`, `delay`, `ease`, `times`, `repeat`, springs, `inertia`, and more:

```svelte
<div
	{@attach mercury({
		animate: { scale: 1.5 },
		transition: { ease: 'circInOut', duration: 1, delay: 0.3 }
	})}
/>
```

Override it per value, with `default` for the rest:

```svelte
<div
	{@attach mercury({
		animate: { x: 100, opacity: 1 },
		transition: { default: { type: 'spring' }, opacity: { duration: 0.2, ease: 'linear' } }
	})}
/>
```

A target can carry its own `transition`, which wins over the attachment's: `whileHover: { scale: 1.1, transition: { duration: 0.1 } }`. `transitionEnd` sets values once the animation has finished:

```svelte
<div {@attach mercury({ animate: { opacity: 0, transitionEnd: { display: 'none' } } })} />
```

### Easing

Pass any [Motion easing](https://motion.dev/docs/easing-functions) as `ease`:

- **Named easings:** `linear`, `easeIn`, `easeOut`, `easeInOut`, `circIn`, `circOut`, `circInOut`, `backIn`, `backOut`, `backInOut`, `anticipate`
- **Cubic bezier:** `ease: [0.39, 0.24, 0.3, 1]`
- **Functions:** `ease: (progress) => progress * progress`, or Motion's `steps(4)`, `cubicBezier()`…
- **Per keyframe:** an array of easings, one between each pair of keyframes

### Repetition

- `repeat`: number of repetitions (`Infinity` supported).
- `repeatType`: `'loop'`, `'reverse'` or `'mirror'`.
- `repeatDelay`: delay in seconds between repetitions.

```svelte
<div
	{@attach mercury({
		animate: { backgroundColor: ['#ff0088', '#0d63f8'] },
		transition: { duration: 2, repeat: Infinity, repeatType: 'reverse', ease: 'linear' }
	})}
></div>
```

### Springs

Set `type: 'spring'`. Like Motion, springs are configured in one of two ways:

- **Duration-based** — `duration` (or `visualDuration`, the time to visually reach the target) and `bounce` (`0` to `1`, default `0.25`).
- **Physics-based** — `stiffness`, `damping` and `mass`, plus `velocity`, `restSpeed` and `restDelta`. These pick up the velocity of an interrupted animation or gesture.

```svelte
{@attach mercury({
	animate: { rotate: 180 },
	transition: { type: 'spring', stiffness: 150, damping: 20 }
})}
```

:::warning[Don't mix the two]
`duration` and `bounce` are ignored the moment any of `stiffness`, `damping`, or `mass` is set. Pick one configuration style per spring.
:::

## Stagger

Give each element its own delay:

```svelte
{#each { length: 3 }, i}
	<div
		{@attach mercury({
			animate: { opacity: 1, y: [64, 0] },
			transition: { delay: i * 0.05 }
		})}
	/>
{/each}
```

To stagger children from a parent — and in reverse on the way out — use [variants](/docs/api/variants) with `staggerChildren`.

## Animatable values

Anything Motion animates: transforms as independent values (`x`, `y`, `scale`, `rotate`, `skewX`…), colors, CSS variables (`'--progress'`), `filter`, `clipPath`, `boxShadow`, `width` / `height` (including to and from `'auto'`), and SVG attributes such as `pathLength`, `pathOffset`, `cx` or `d` on SVG elements:

```svelte
<svg viewBox="0 0 100 100">
	<path
		d="M10 50 L90 50"
		{@attach mercury({ initial: { pathLength: 0 }, animate: { pathLength: 1 } })}
	/>
</svg>
```

See [Motion's docs](https://motion.dev/docs/animate) for the full list.

## Events

These mirror Motion's component events:

- `onAnimationStart(definition)` — an animation started.
- `onAnimationComplete(definition)` — it finished. `definition` is the target or variant label.
- `onUpdate(latest)` — every frame, with the latest values.

```svelte
{@attach mercury({
	animate: { x: 100 },
	onAnimationStart: () => console.log('started'),
	onAnimationComplete: (definition) => console.log('done', definition)
})}
```

## Playback controls

For imperative control, pass `instance`. It's called with the playback controls of the animations each change starts — `play()`, `pause()`, `stop()`, `cancel()`, a `finished` promise, a `completed` flag, and Motion's underlying `controls`:

```svelte
<script lang="ts">
	import { mercury, type AnimationInstance } from '@omicrxn/mercury';
	let animation = $state<AnimationInstance>();
</script>

<div
	{@attach mercury({
		animate: { scale: 1.5 },
		transition: { duration: 1 },
		instance: (i) => (animation = i)
	})}
></div>

<button onclick={() => animation?.play()}>Play</button>
<button onclick={() => animation?.pause()}>Pause</button>
```

For timelines and sequences, use Motion's [`animate()`](https://motion.dev/docs/animate) directly — on an element Mercury animates it shares the same motion values.
