Skip to main content

Command Palette

Search for a command to run...

Svelte transitions – tried all 7 built-in ones

Published
4 min read

TL;DR

Svelte has 7 built-in transitions (fade, blur, fly, slide, scale, draw, crossfade). I tried each one with short examples and GIFs. Use this as a quick reference when you need a transition.

Intro

While working on a Svelte project, I learned that Svelte has its own transition syntax. I tried all seven built-in transitions and noted how each behaves.

The transitions

1. fade

<button onclick={() => (visibleFade = !visibleFade)}>Toggle</button>
{#if visibleFade}
  <div transition:fade={{ duration: 300 }}>Fade</div>
{/if}

2. blur

<button onclick={() => (visibleBlur = !visibleBlur)}>Toggle</button>
{#if visibleBlur}
  <div transition:blur={{ duration: 400, amount: 8 }}>Blur</div>
{/if}

3. fly

<button onclick={() => (visibleFly = !visibleFly)}>Toggle</button>
{#if visibleFly}
  <div transition:fly={{ y: 20, duration: 400 }}>Fly</div>
{/if}

4. slide

<button onclick={() => (visibleSlide = !visibleSlide)}>Toggle</button>
{#if visibleSlide}
  <div transition:slide={{ duration: 400, axis: 'y' }}>Slide</div>
{/if}

5. scale

<button onclick={() => (visibleScale = !visibleScale)}>Toggle</button>
{#if visibleScale}
  <div transition:scale={{ duration: 400, start: 0, opacity: 0.5 }}>Scale</div>
{/if}

6. draw

<button onclick={() => (visibleDraw = !visibleDraw)}>Toggle</button>
<svg viewBox="0 0 100 40" width="120" height="48" aria-hidden="true">
  {#if visibleDraw}
    <path
      transition:draw={{ duration: 800 }}
      fill="none"
      stroke="var(--color-accent, #89b4fa)"
      stroke-width="2"
      stroke-linecap="round"
      d="M 10 20 Q 50 5 90 20"
    />
  {/if}
</svg>

7. crossfade

As the docs say:

The crossfade function creates a pair of transitions called send and receive.

So crossfade is different: it works with two elements (one leaving, one entering). Use the same key on both so Svelte treats them as the same content moving. Fallback is used when there’s no pair (e.g. first load); here we use fade so it still fades in/out instead of popping.

<script>
	const [send, receive] = crossfade({
		duration: 400,
		fallback: (node, _params, intro) => fade(node, { duration: 400 })
	});
</script>
<button onclick={() => (crossfadePosition = crossfadePosition === 0 ? 1 : 0)}>
  Move left ↔ right
</button>
<div>
  <div>
    {#if crossfadePosition === 0}
      <div in:receive={{ key: 'same' }} out:send={{ key: 'same' }}>
        Moves
      </div>
    {/if}
  </div>
  <div>
    {#if crossfadePosition === 1}
      <div in:receive={{ key: 'same' }} out:send={{ key: 'same' }}>
        Moves
      </div>
    {/if}
  </div>
</div>

8. Bonus - Custom transition

If you need something custom, you can use the transition contract and build your own.

<script lang="ts">
	function spinFade(node: HTMLElement, { duration = 500, delay = 0, turns = 1 }: SpinFadeParams) {
		const style = getComputedStyle(node);
		const opacity = +style.opacity;
		return {
			delay,
			duration,
			css: (t: number) => {
				const angle = (1 - t) * turns * 360;
				return `opacity: \({t * opacity}; transform: rotate(\){angle}deg);`;
			}
		};
	}
</script>

<button onclick={() => (visibleCustom = !visibleCustom)}>Toggle</button>
{#if visibleCustom}
  <div transition:spinFade={{ duration: 500, turns: 1 }}>Custom</div>
{/if}

Quick comparison

Transition Best for Note
fade Simple show/hide Easiest, good default
blur Focus / attention Use amount to control
fly Element “moves into” the place x, y, opacity
slide Element “expands/collapses” axis: 'x' or 'y'
scale Zoom in/out feel start, opacity
draw SVG paths only Stroke animation
crossfade Moving content between containers Needs send / receive

Conclusion

  • fly = element moves in; slide = element grows/shrinks in place.

Easy to wire up and no extra dependency – Probably I’d use them in a real product. For simple show/hide or list changes, the built-ins are enough; if you need something fancy, the custom transition API is still straightforward. Worth trying when you want a bit of polish without much code. 😆