Easy-Peasy Page Transitions in SvelteKit 🎨✨
Learn how to spice up your SvelteKit app with slick page transitions using Svelte’s built-in transition and animation magic. Boost that user experience with eye-catching visuals between pages. 🚀
Overview 🌟
In today’s modern web scene, smooth page transitions can seriously level up your user experience. Lucky for us, SvelteKit makes it super easy to pull this off thanks to Svelte’s powerful built-in animation and transition APIs. In this guide, we’re gonna break down how to add buttery-smooth page transitions to your SvelteKit app. Let’s roll! 🎉
Prerequisites 🛠️
Before diving in, make sure you’ve got the following under your belt:
- Basic knowledge of Svelte and SvelteKit.
- A working SvelteKit project. If not, no worries — just spin one up like this:
npx sv create my-sveltekit-app cd my-sveltekit-app npm install npm run dev
Step 1: Install What’s Needed 📦
We’re keeping it lean — no extra packages here. We’ll be using svelte/transition
and svelte/easing
, which come out of the box with Svelte. Easy win.
Step 2: Set Up a Layout Component 🏗️
To get transitions going across all pages, we’ll tweak the +layout.svelte
file — this is basically the wrapper for all your routes.
- Open or create
src/routes/+layout.svelte
. - Throw in the following code to wire up the layout with a transition:
<script>
import { fade } from 'svelte/transition';
let { children, data } = $props();
let { pathname } = $derived(data);
</script>
{#key pathname}
<div transition:fade>
{@render children()}
</div>
{/key}
What’s Happening:
transition:fade
: Applies a fade effect when switching pages.{@render children()}
: This is where the current page content gets rendered.
But hang tight — we need pathname
to make this work. Time to bring in the load
function.
Step 3: Grab the Pathname Using load
🔄
The load
function is where we can fetch info about the current URL. We’ll use it to get the pathname
and pass it into our layout.
- Create a new file at
src/routes/+layout.ts
:
export const load: LayoutLoad = async ({ url }) => {
const { pathname } = url;
return {
pathname
};
};
- Use
pathname
in your+layout.svelte
:
<script>
import { fade } from 'svelte/transition';
let { children, data } = $props();
let { pathname } = $derived(data);
</script>
{#key pathname}
<div in:fade={{ duration: 300, delay: 400 }} out:fade={{ duration: 300 }}>
{@render children()}
</div>
{/key}
What’s Going On:
in:fade
andout:fade
: Sets up enter and exit animations with custom timing.delay
: Adds a small pause for that smooth handoff between pages.
Step 4: Make It Pop with Easing 🎢
Want your transitions to feel a bit more alive? Add some easing curves. Svelte’s got your back with a bunch of built-in options.
- Import easing from
svelte/easing
:
<script>
import { fade } from 'svelte/transition';
import { cubicIn, cubicOut } from 'svelte/easing';
let { children, data } = $props();
let { pathname } = $derived(data);
</script>
{#key pathname}
<div
in:fade={{ easing: cubicOut, duration: 300, delay: 400 }}
out:fade={{ easing: cubicIn, duration: 300 }}
>
{@render children()}
</div>
{/key}
What’s the Deal:
cubicIn
andcubicOut
: These easing curves give your transitions a more natural feel.
Step 5: Try Out the fly
Effect ✈️
Feeling fancy? Swap fade
with fly
to add motion to your page transitions.
<script>
import { fly } from 'svelte/transition';
import { cubicIn, cubicOut } from 'svelte/easing';
let { children, data } = $props();
let { pathname } = $derived(data);
</script>
{#key pathname}
<div
in:fly={{ easing: cubicOut, y: 10, duration: 300, delay: 400 }}
out:fly={{ easing: cubicIn, y: -10, duration: 300 }}
>
{@render children()}
</div>
{/key}
What’s What:
y: 10
andy: -10
: Controls the direction of the fly animation (up or down).
Optional: Add a Loading Bar ⏳
For that extra polish, show a loader while the next page is loading. Super handy if your routes are doing any data-fetching or just need a sec.
<script>
import { beforeNavigate, afterNavigate } from '$app/navigation';
let isLoading = $state(false);
let loadingProgress = $state(0);
let loadingInterval: ReturnType<typeof setInterval> | null = null;
beforeNavigate(({ to }) => {
if (to?.route.id) {
isLoading = true;
loadingProgress = 0;
loadingInterval = setInterval(() => {
loadingProgress = Math.min(90, loadingProgress + 5);
}, 100);
}
});
afterNavigate(() => {
if (loadingInterval) {
clearInterval(loadingInterval);
loadingInterval = null;
}
loadingProgress = 100;
setTimeout(() => {
isLoading = false;
}, 300);
});
</script>
{#if isLoading}
<div class="absolute inset-x-0 top-0 z-10">
<div class="h-1 w-full bg-slate-200 dark:bg-slate-700">
<div
class="h-full animate-pulse bg-slate-700 transition-all duration-300 dark:bg-slate-400"
style="width: {loadingProgress}%"
></div>
</div>
</div>
{/if}
What’s Up Here:
beforeNavigate
andafterNavigate
: Used to toggle a loading state during navigation.
Bonus Info: This Website Uses It Too 🌐
Yep, the website you’re reading this on? It uses the same kind of page transitions we’re talking about here. A loading bar while pages change, so users always know something’s happening behind the scenes.
Wrapping It Up 🎯
Adding smooth page transitions to your SvelteKit app is a dead-simple way to bump up the UX and make your app feel way more pro. Thanks to Svelte’s built-in transition and animation tools, it’s super quick to get started. Try out different effects, tweak some timings, and make your app really stand out. 🚀
Happy coding, and may your transitions be silky-smooth and jaw-dropping! ✨🎉