Transitions and animations

Svelte · Advanced · 12 min

Built-in motion makes interfaces feel polished with a single attribute instead of an animation library.

Svelte has motion built in.

Transitions play when an element enters or leaves the DOM because of a block ({#if}, {#each}, {#key}):

  • transition:fade plays both ways; in:fly={{ y: 20 }} and out:fade set them separately.
  • Built-ins in svelte/transition: fade, fly, slide, scale, blur, draw (SVG paths) and crossfade (send/receive pairs that move an element between lists).
  • Parameters such as duration, delay and easing (from svelte/easing) are passed as an object.
  • Transitions are local by default: they run only when their own block changes, not when a parent block is created or destroyed. Add |global to change that.
  • Events onintrostart, onoutroend etc. let you react to them.
  • Most built-in transitions are implemented with CSS animations, so they run off the main thread.

`animate:flip` (from svelte/animate) smoothly moves items in a keyed {#each} when they reorder.

Motion values: Tween and Spring classes from svelte/motion (5.8+) animate a number or object over time; set .target and read .current in markup, for animated counters, progress bars and charts.

Respect users' reduced-motion settings; prefersReducedMotion from svelte/motion is a reactive boolean to check.

Custom transitions are functions returning { duration, css: (t, u) => '...' } or a tick callback.

Example

<script>
  import { fade, fly } from 'svelte/transition';
  import { flip } from 'svelte/animate';
  import { Tween } from 'svelte/motion';
  import { cubicOut } from 'svelte/easing';

  let show = $state(true);
  let items = $state([1, 2, 3, 4]);
  const progress = new Tween(0, { duration: 400, easing: cubicOut });
</script>

<button onclick={() => (show = !show)}>toggle</button>
{#if show}
  <p in:fly={{ y: 20, duration: 300 }} out:fade>Hello!</p>
{/if}

<button onclick={() => items.reverse()}>shuffle</button>
{#each items as n (n)}
  <div animate:flip={{ duration: 250 }}>{n}</div>
{/each}

<button onclick={() => (progress.target = Math.random())}>go</button>
<progress value={progress.current}></progress>

Remember

  • transition:, in:, out: animate elements added or removed by blocks
  • Transitions are local by default; add |global to play with parent blocks
  • animate:flip needs a keyed each; Tween/Spring animate values

Watch out for

  • Expecting animate:flip to work on an unkeyed {#each}; it requires a key.

Check yourself

  1. A `<p transition:fade>` is inside an `{#if a}` which is inside `{#if b}`. b becomes false. Does the fade play?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: svelte.dev/docs/svelte/transition