A simple page transition indicator in SvelteKit

Posted on:

In applications that uses client side routing, it can be confusing to users between page transition when there are no indicators that shows a new page is being loaded. In this tutorial, we will learn how to add a simple page transition indicator to a SvelteKit app.

Setup

First we will need to create a new SvelteKit application:

  1. run npm init svelte my-app and follow the prompts to setup a new SvelteKit application.
  2. cd my-app
  3. npm install

After the App is created, we can start the dev server with npm run dev -- --open and the app is live at http://localhost:3000.

Add a new page

To demonstrate page transitions, we will need at least two pages. So let add a new page by creating a new file about.svelte under src/routes. SvelteKit uses filesystem based routing, so a file under src/routes named about.svelte will add a route /about.

<h1>About Page</h1>

<a href="/">Back Home</a>

Then we can link to this new about page from our home page (src/routes/index.svelte):

<h1>Home</h1>

<a href="/about">About Page</a>

Since the page is so simple, it's takes no time to transition from one page to another. Page transition indicator is useful if the page takes some time to load, so we will simulate a slow page by adding a delay to the /about page:

In src/routes/about.svelte file:

<script lang="ts" context="module">
  import type { Load } from '@sveltejs/kit';

  // A dummy load function that takes a while to complete
  export const load: Load = async () => {
    return new Promise((resolve) => {
      const res = () =>
        resolve({
          status: 200
        });
      setTimeout(res, 1000);
    });
  };
</script>

The load function in about page will be called when the page is requested and we added a 1 second delay to return the page content.

If we now go to the home page and click on About Page link, it will looks like nothing is happening and then the new content is shown instantly, which is confusing. So let's add a page transition indicator to let the user know that the new page is being loaded.

SvelteKit Layout

SvelteKit supports layout files that allows users to nest pages inside a layout page. Since we want to add page transition indicator to all pages, we can use a layout file instead of modifying every page to support the transition.

In SvelteKit, layout files are routes with a specific naming convention: the file must starts with __layout (note it's double _). To add a layout to all pages, add a file named __layout.svelte under src/routes:

<div class="wrapper">
  <slot />
</div>

SvelteKit will replace the <slot /> slot with the rendered page content. Applying this layout, our home page actually renderrs like:

<div class="wrapper">
  <h1>Home</h1>
  <a href="/about">About Page</a>
</div>

Notice that the content rendered from index.svelte is wrapped with the div from the layout file. This also happens to the /about page.

With this in place, we can now add an page transition indicator. But how do we know when pages are transitioning? That turns out to be pretty easy in SvelteKit.

SvelteKit lifecycle hooks

Pages in SvelteKit are essentially Svelte components. Like all other Svelte components, pages (and layout) has lifecyle hooks that we can use to run certain code. SvelteKit additionally provides hooks when navigation events are fired. In this tutorial, we will use the beforeNavigate and afterNavigate lifecycle hooks form $app/navigation module.

Add the following script section to __layout.svelte file:

<script lang="ts">
  import { afterNavigate, beforeNavigate } from '$app/navigation';
  import { fade } from 'svelte/transition';

  // declare a reactive property that will change to `true` when navigation is running
  $: loading = false;

  beforeNavigate(() => {
    loading = true;
  });

  afterNavigate(() => {
    loading = false;
  });
</script>

Now we can use the loading property to render an transition indicator when navigation starts and hide it when navigation is done. But first, let's create a simple loading indicator component.

Loading Indicator

It's up to you how you want to implement this loading indicator, but in this example, we will use a simple animated SVG. Specifically ball-triangle.svg from the collection of animated SVG icons created by Sam Herbert.

Create a file named Loading.svelte under src/lib/components:

<svg width="57" height="57" viewBox="0 0 57 57" xmlns="http://www.w3.org/2000/svg" stroke="#fff">
  <!-- omitted-->
</svg>

Then in __layout.svelte file, we can conditionally render the loading indicator when loading is true :

<div class="wrapper">
  {#if loading}
  <div class="modal">
    <Loading />
  </div>
  {/if}
  <slot />
</div>

We will also add some styling to make the indicator look like a modal:

In __layout.svelte file, add a <style> section:

<style>
  .modal {
    display: grid;
    place-items: center;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(26, 18, 25, 0.63);
    backdrop-filter: blur;
  }
</style>

Finally, we can make the showing and hiding of the indicator smoother by adding a subtle fade transition:

<div class="modal" transition:fade>
  <Loading />
</div>

Results

That's it! With just a few lines of code we've added a slick transition indicator to our SvelteKit application. The final result looks like:

Image showing the page transition

You can also see the live demo and get the source code on Github.