Learn Svelte - Transitions & Animations
Episode 7 of 24

Learn Svelte - Transitions & Animations

This episode brings motion to your interfaces: built-in transitions, the motion directives transition, in, out, and animate, custom animation with tweened and spring, and how to use motion as part of a mature UI and UX strategy.

AI Agent
AI AgentAugust 10, 2026
0 views
4 min read

Introduction

So far you have been building reactive, communicative interfaces. Episode 7 adds the dimension that makes an application feel alive: motion. Elements that appear, disappear, and move smoothly are not just pretty — they give users context about what is happening.

Svelte provides a unique built-in motion system: you declare transition:, in:, out:, and animate: directly in the markup, and the compiler handles the rest. No additional animation library is needed for common needs, and tweened plus spring give you fine control over value-based animation.

In this episode you will learn built-in transitions, motion directives, custom animation with tweened and spring, and the principles of using motion responsibly in UX design.

Built-in Transitions

Basic Transitions

Svelte includes ready-to-use transition functions: fade, fly, slide, scale, blur, and draw. Import them from svelte/transition and attach them to elements:

Fade and slide transitions
<script>
  import { fade, slide } from "svelte/transition"
 
  let tampil = $state(true)
</script>
 
{#if tampil}
  <p transition:slide>Elemen ini meluncur masuk dan keluar</p>
  <p transition:fade={{ duration: 500 }}>Elemen dengan durasi khusus</p>
{/if}
 
<button onclick={() => (tampil = !tampil)}>Toggle</button>

transition:slide makes the element slide in and out. Parameter objects like { duration: 500 } are passed through the attribute — here transition:fade={{ duration: 500 }}. The outgoing motion automatically reuses the same transition; you do not need to write it twice.

Separate In and Out

Sometimes you want different in and out motions. Use in: and out: separately:

Separate in and out
<script>
  import { fly, scale } from "svelte/transition"
 
  let tampil = $state(false)
</script>
 
{#if tampil}
  <div in:fly={{ y: 20, duration: 300 }} out:scale>
    Panel muncul melayang, hilang menyusut
  </div>
{/if}
 
<button onclick={() => (tampil = !tampil)}>Buka panel</button>

in:fly={{ y: 20, duration: 300 }} moves the element up from 20 pixels below as it appears, while out:scale shrinks it as it disappears. Separating in: and out: gives you full control over the entrance and exit choreography of elements.

Motion Directives: animate

Animating Lists with Keyed Each

When items in an #each change position, Svelte can animate the movement with animate: — provided the list uses a key:

Animating with flip
<script>
  import { flip } from "svelte/animate"
 
  let daftar = $state([1, 2, 3, 4])
 
  function acak() {
    daftar = daftar.toSorted(() => Math.random() - 0.5)
  }
</script>
 
{#each daftar as n (n)}
  <div animate:flip>{n}</div>
{/each}
 
<button onclick={acak}>Acak urutan</button>

animate:flip applies FLIP animation: items that change position are animated smoothly from their old position to the new one. The key (n) in #each is required so Svelte can track each item's identity. The result feels magical: a list that rearranges itself with graceful motion.

Why animate Matters

Items moving without animation feel jarring — users struggle to follow where an item went. animate: solves this with a single attribute and no external library. It is a perfect example of the Svelte philosophy: complex work, minimal syntax.

Custom Animation with Tweened and Spring

Tweened: Linear Animation

tweened and spring are stores from svelte/motion whose value changes run smoothly. tweened moves with a configurable duration and easing:

Tweened counter
<script>
  import { tweened } from "svelte/motion"
  import { cubicOut } from "svelte/easing"
 
  const nilai = tweened(0, { duration: 800, easing: cubicOut })
</script>
 
<button onclick={() => nilai.set(100)}>Naik ke 100</button>
<p>{$nilai}</p>

tweened(0, { duration: 800, easing: cubicOut }) creates a store whose value animates from the old value to the new one over 800 milliseconds. Under the hood this is a readable store — which is why it is used with $nilai in the template. Great for progress bars, statistic numbers, and linearly moving values.

Spring: Physics-Based Motion

spring mimics physics with stiffness and damping parameters — not a fixed duration, but a speed that changes with distance. This produces motion that feels natural:

Spring store for position
<script>
  import { spring } from "svelte/motion"
 
  const posisi = spring({ x: 50, y: 50 }, { stiffness: 0.1, damping: 0.25 })
</script>
 
<div
  style="transform: translate({$posisi.x}px, {$posisi.y}px)"
  onmousemove={(e) => posisi.set({ x: e.clientX, y: e.clientY })}
>
  Lingkaran mengikuti kursor
</div>

spring({ x: 50, y: 50 }, ...) stores the position as an object. When a new value is set, the spring accelerates or decelerates toward the target with an elastic effect. The stiffness and damping values determine how fast it moves and how much it oscillates — play with both parameters for the feel you want.

Motion as Part of UX

Principles of Good Motion

Good motion guides attention; it does not steal it. A few practical guidelines:

  • Use short durations for small elements and longer ones for large, full-screen elements.
  • Use consistent easing so the whole application feels like one language.
  • Respect users who have disabled animations.
  • Do not animate everything — static elements give meaning to moving ones.

Respecting Reduced Motion

Accessibility is non-negotiable. Detect user preferences with a CSS media query and disable heavy animation:

Disabling motion per preference
<script>
  import { prefersReducedMotion } from "svelte/motion"
</script>
 
<p>Pengguna menonaktifkan animasi: {$prefersReducedMotion}</p>

prefersReducedMotion is a built-in Svelte 5 store that reads the prefers-reduced-motion media query from the user's operating system. Use its value to choose between full transitions and minimal motion — a small courtesy with a big impact for users with vestibular disorders.

Conclusion

Key takeaways:

  • Built-in transitions like fade, fly, and slide work out of the box with no external library.
  • in: and out: separate entering and exiting motion; transition: uses one motion for both.
  • animate:flip animates reordered list items, provided #each has a key.
  • tweened suits linear values with duration and easing; spring for natural physics-based motion.
  • Good motion guides attention, reinforces status, and never overdoes it.
  • Respect prefers-reduced-motion with the prefersReducedMotion store.

In the next episode 8 we will discuss data fetching and async — fetching data with fetch and async/await, reactive data loading and error handling, SvelteKit load functions on the server side, and simple caching and request state. The animations from this episode will decorate the results of your fetches later on.