Svelte
04 / 07

Transitions & Animations

Svelte Transitions & Animations

Svelte makes it easy to add smooth transitions and animations to your app with built-in transition directives.

Built-in Transitions

<script>
  import { fade, fly, slide, scale, blur, draw } from 'svelte/transition';
  import { quintOut } from 'svelte/easing';
  
  let visible = true;
</script>

<!-- Fade -->
{#if visible}
  <p transition:fade>Fades in and out</p>
{/if}

<!-- Fly -->
{#if visible}
  <p transition:fly="{{ y: 200, duration: 500 }}">Flies in from below</p>
{/if}

<!-- Slide -->
{#if visible}
  <p transition:slide>Slides in and out</p>
{/if}

<!-- Scale -->
{#if visible}
  <p transition:scale="{{ start: 0, duration: 500, easing: quintOut }}">
    Scales in and out
  </p>
{/if}

<!-- Separate in and out transitions -->
{#if visible}
  <p in:fly="{{ y: 200 }}" out:fade>Different transitions</p>
{/if}

<!-- Local transitions (don't play on initial render) -->
{#if visible}
  <p transition:fade|local>Only when toggled, not on mount</p>
{/if}

<button on:click={() => visible = !visible}>Toggle</button>

Custom Transitions

<script>
  function customTransition(node, { duration = 400 }) {
    return {
      duration,
      css: t => {
        const eased = quintOut(t);
        return `
          opacity: ${eased};
          transform: scale(${eased}) rotate(${eased * 360}deg);
        `;
      }
    };
  }
  
  // JavaScript-based transition
  function typewriter(node, { speed = 50 }) {
    const text = node.textContent;
    const duration = text.length * speed;
    
    return {
      duration,
      tick: t => {
        const i = Math.trunc(text.length * t);
        node.textContent = text.slice(0, i);
      }
    };
  }
</script>

{#if visible}
  <p transition:customTransition>Custom transition</p>
  <p in:typewriter="{{ speed: 30 }}">Typing effect...</p>
{/if}

Animations

<script>
  import { flip } from 'svelte/animate';
  import { quintOut } from 'svelte/easing';
  
  let todos = [
    { id: 1, text: 'Todo 1', done: false },
    { id: 2, text: 'Todo 2', done: false }
  ];
  
  function remove(id) {
    todos = todos.filter(t => t.id !== id);
  }
</script>

<!-- Animate list reordering -->
{#each todos as todo (todo.id)}
  <div animate:flip="{{ duration: 300, easing: quintOut }}">
    {todo.text}
    <button on:click={() => remove(todo.id)}>×</button>
  </div>
{/each}

Motion (Tweened & Spring)

<script>
  import { tweened } from 'svelte/motion';
  import { spring } from 'svelte/motion';
  import { cubicOut } from 'svelte/easing';
  
  // Tweened - smooth interpolation
  const progress = tweened(0, {
    duration: 400,
    easing: cubicOut
  });
  
  // Spring - physics-based motion
  const coords = spring(
    { x: 0, y: 0 },
    {
      stiffness: 0.1,
      damping: 0.25
    }
  );
  
  function setProgress(value) {
    progress.set(value);
  }
</script>

<progress value={$progress} max="100"></progress>
<button on:click={() => setProgress(100)}>Complete</button>

<!-- Follow mouse with spring -->
<svg on:mousemove={(e) => coords.set({ x: e.clientX, y: e.clientY })}>
  <circle cx={$coords.x} cy={$coords.y} r="10" />
</svg>

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free