# Svelte transitions – tried all 7 built-in ones

## **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.

*   Docs: [Template syntax](https://svelte.dev/docs/svelte/transition), [Reference](https://svelte.dev/docs/svelte/svelte-transition)
    

## The transitions

### 1\. fade

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

![](https://cdn.hashnode.com/uploads/covers/65ab3be4d775f416312d4036/41562c1a-fc21-4176-803e-6837eb24f2aa.gif align="center")

### 2\. blur

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

![](https://cdn.hashnode.com/uploads/covers/65ab3be4d775f416312d4036/b94ba11b-e50b-410d-b35f-27d13d73f666.gif align="center")

### 3\. fly

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

![](https://cdn.hashnode.com/uploads/covers/65ab3be4d775f416312d4036/fddf34d5-2680-4fad-b074-7055c3c8d50e.gif align="center")

### 4\. slide

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

![](https://cdn.hashnode.com/uploads/covers/65ab3be4d775f416312d4036/43b8bb1c-b2e5-4a57-a874-678cbd9300cf.gif align="center")

### 5\. scale

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

![](https://cdn.hashnode.com/uploads/covers/65ab3be4d775f416312d4036/953e0039-da59-43c7-aa7e-16c95f54d41b.gif align="center")

### 6\. draw

```svelte
<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>
```

![](https://cdn.hashnode.com/uploads/covers/65ab3be4d775f416312d4036/2f173d6c-01ab-4d81-b3ad-cd7c9a70bc6d.gif align="center")

### 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.

```svelte
<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>

```

![](https://cdn.hashnode.com/uploads/covers/65ab3be4d775f416312d4036/26bd992f-d331-4941-8d9a-94e257698c21.gif align="center")

### 8\. Bonus - Custom transition

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

```svelte
<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}

```

![](https://cdn.hashnode.com/uploads/covers/65ab3be4d775f416312d4036/76227eb3-f3d1-40a8-8ec4-9128635c54e0.gif align="center")

## 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. 😆
