How to do page transitions with svelte/sapper

Let me just start of by saying I don't know if this is the most effective way to do it. This is the way I solved it and it works great for me.

I first made my own custom variation of the 'fade' transition and put it in 'components/pageFade.js'

import { sineOut } from "svelte/easing";

let duration = 250;
let delay = duration;

let delayZero = 0;

export const fadeIn = _ => ({
  duration,
  delay,
  easing: sineOut,
  css: t => `opacity: ${t}`
});
export const fadeOut = _ => ({
  duration,
  delay: delayZero,
  easing: sineOut,
  css: t => `opacity: ${t}`
});
  1. The delayZero variable is there because for some reason it won't take '0' directly and will break.
  2. The duration variable is the length of the fade in milliseconds

Then for all files I wanted to have this transition on I did the following:

<script>
  import { fadeIn, fadeOut } from "../components/pageFade";

  // All your other JS goes here
</script>

<style>
  /* Styles go here */
</style>

<main in:fadeIn out:fadeOut>
  <!-- All the normal HTML goes here -->
</main>

I would then use that as a template on almost every single one of the pages, which seems like a lot but it's not too bad.

Hope it helps and let me know if you have any other questions!

Max


FYI @max-larsson, from your code, fadeIn is defined as a function which needs no arguments and returns an object having a duration property equal to the value of the duration variable at this scope. same with delay. Note easing is defined as the name, with the value of sineOut.

This may be your issue if you just tried to put a literal 0 where delay was. I see you tried making delayZero, but used it likely in a different way than you intended. You probably meant to write this:

export const fadeOut = _ => ({
  duration,
  delay: 0,
  easing: sineOut,
  css: t => `opacity: ${t}`
});

When I tried this, it works just fine for me. Thanks for sharing your example.


If you want to include transition in _layout.svelte and don't need to include them in every route here is an alternative.

Here is a simple fly in/out from top transition.

<!-- src/component/PageTransitions.svelte -->
<script>
  import { fly } from 'svelte/transition';
  export let refresh = '';
</script>

{#key refresh}
  <div
    in:fly="{{ y: -50, duration: 250, delay: 300 }}"
    out:fly="{{ y: -50, duration: 250 }}" 
    >
    <slot/>
  </div>
{/key}

And here is the layout component

<!-- src/routes/$layout.svelte for Svelte@next -->
<!-- src/routes/_layout.svelte for Sapper -->

<script>
  import Nav from '../components/Nav';
  import PageTransitions from '../components/PageTransitions';
  export let segment;   
</script>

<Nav {segment}/>

<PageTransitions refresh={segment}>
  <slot/>
</PageTransitions>

And for completeness here is a simple Nav component

<!-- src/component/Nav.svelte -->
<script>
    export let segment;
</script>

<style>
  a {
    text-decoration: none;
  }
  .current {
    text-decoration: underline;
  }
</style>
<div>
  <a href="/" class='{segment === undefined ? "current" : ""}'>Home</a>
  <a href="/about" class='{segment === "about" ? "current" : ""}'>About</a>
</div>

NOTES: We make the component reactive by creating a refresh prop and using key directive which means that when the key changes, svelte removes the component and adds a new one, therefore triggering the transition.

In the layout component we pass the segment (route) as refresh prop and therefore the refresh key changes as the route changes.

Here is a demo of the sample code above and the github repo