SvelteKit Page Transitions

Published Apr 7, 2023

Table of Contents

SvelteKit Transitions

In SvelteKit a page is just a component meaning whatever you’re able to do with components you can do to a page which makes transitions simple.

Before you can add the transition you have to know when a page has changed which you can do by exporting a url prop from a +layout.ts or +layout.server.ts file.

src/routes/+layout.ts
export function load({ url }) {
  return {
    url: url.pathname,
  }
}

If you’re curious why I’m not using the $page store you can watch the video where I explain why it doesn’t work.

Inside the root layout you can wrap <slot/> with a #key ... block which takes a value that when changed destroys and recreates the contents and plays the transition.

src/routes/+layout.svelte
<script lang="ts">
  import { fly } from 'svelte/transition'

  export let data
</script>

{#key data.url}
  <div
    in:fly={{ x: -200, duration: 300, delay: 300 }}
    out:fly={{ x: 200, duration: 300 }}
  >
    <slot />
  </div>
{/key}

The delay is required for the entering transition to not overlap with the leaving transition if you use both or transition:fly which is the same thing.

You’re done! 🥳

Custom Transitions

You can spice up your transition by creating a custom transition in Svelte which is just a Svelte action that returns a transition object.

I’m going to create a transition.svelte component that uses a flush transition.

src/lib/transition.svelte
<script lang="ts">
  import { cubicIn } from 'svelte/easing'
  import type { EasingFunction, TransitionConfig } from 'svelte/transition'

  import { playFlush } from '$lib/sounds'

  export let key: string
  export let duration = 300

  type Params = {
    delay?: number
    duration?: number
    easing?: EasingFunction
  }
  type Options = {
    direction?: 'in' | 'out' | 'both'
  }

  function flush(
    node: Element,
    { delay = 0, duration = 300, easing = cubicIn }: Params = {},
    { direction = 'both' }: Options = {}
  ): TransitionConfig {
    direction === 'out' && playFlush()

    return {
      delay,
      duration,
      easing,
      css: (t) => `
        scale: ${t};
        rotate: ${t}turn;
      `,
    }
  }
</script>

{#key key}
  <div
    in:flush={{ duration, delay: duration }}
    out:flush={{ duration }}
  >
    <slot />
  </div>
{/key}

Here’s the code for the sound if you want to give it a try.

src/lib/sounds.ts
export function playFlush() {
  const url = 'https://cdn.pixabay.com/audio/2021/08/09/audio_e2a6340055.mp3'
  const audio = new Audio(url)
  audio.volume = 0.5
  audio.play()
}

This type of transition creates a regular CSS animation from the css function you return where you get access to t which is a value from 0 to 1 and u which is the opposite meaning it goes from 1 to 0.

You can think of t as time but it’s not the duration of the transition but the end and start. An easing for example is just some fancy math that changes the numbers in that range to something else. Here’s a great ease visualizer for easing functions in Svelte.

This is also how the default Svelte transitions are implemented which you can find in the documentation.

src/routes/+layout.svelte
<script lang="ts">
  import PageTransition from '$lib/transition.svelte'

  export let data
</script>

<PageTransition key={data.url} duration={2000}>
  <slot />
</PageTransition>

You can name the imported component anything you want.

Support

You can subscribe on YouTube, or consider becoming a patron if you want to support my work.

Patreon
Found a mistake?

Every post is a Markdown file so contributing is simple as following the link below and pressing the pencil icon inside GitHub to edit it.

Edit on GitHub