| SvelteKit’s load
A look at SvelteKit's load function through a number of examples, and see the different ways it can be used in 1.0.
Jan

SvelteKit’s load function is run prior to the page being rendered, whether it’s done on the server side with SSR or on the client side. It allows us to retrieve data, do redirects, and other processing before our route renders the +page.svelte file. That means we can do things like query our database, call a third-party API or other processing, and make decisions which will affect the output of the final page. That means we don’t have to load the page, then call out, and force a rerender when the data is received. The load function can run on the server, the client or both. If it’s run on the server it’s placed in the +page.server.ts (or .js) file alongside the +page.svelte route file, whereas if it’s to be run on on the client and potentially the server, we use +page.ts. The following examples can be found in this repo, and run by doing:

Running on the client and server

In this example create some JSON-structured data showing a name, email and random number which we return from the load function. This uses Typescript, though if you remove the type annotations and uncomment the first line, you can revert it to plain Javascript.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// /** @type {import('./$types').PageLoad} */ <-- Or this if using +page.js
import type { PageLoad } from './$types';

export const load: PageLoad = async () => {
	console.log('This will run on the server and the client.');

	return {
		person: {
			name: 'Fred',
			email: 'fred@fred.com',
			rnd: Math.floor(Math.random() * 10000)
		}
	};
};

When you return data from a load function it returns to the +page.svelte file in a variable called data, which is shorthand for pulling that same data from page store.

1
2
3
4
5
6
7
<script lang="ts">
	import { page } from '$app/stores';
	export let data: any;
</script>

{data.person.name} | {data.person.email} | {data.person.rnd}
{$page.data.person.name} | {$page.data.person.email} | {$page.data.person.rnd}

If you look in the terminal and your browser’s console while running this you will see messages in each. This load function will execute on both server and client side on the first load, but on consecutive accesses of the route it will only run on the server. You can see this by navigating to a different page on the side and then hitting the back arrow. The console will still show the message, and the random value will be updated, but the terminal will not show that it has run. If you do a hard refresh (ctrl-shift-r) you should see the message again appear in both the terminal and console.

Running on the server only

As already noted, when we want to run it on just the server, we will use the +page.server.ts file and structure our program slightly differently, using the PageServerLoad type.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// /** @type {import('./$types').PageServerLoad} */ <-- Or this if using +page.server.js
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async () => {
	console.log('This will run on the server.');

	return {
		person: {
			name: 'Fred',
			email: 'fred@fred.com',
			rnd: Math.floor(Math.random() * 10000)
		}
	};
};

This is the same program as before, but when you run it you will find that the output only happens in the terminal and not the browser’s console. If you navigate to another page and then back again you will see that the console.log on the server side will update, unlike the above program.

Running on client only

You can force it to run on the client only by using the +page.ts file and then exporting the ssr variable as false. This tells it to turn off any server side rendering on the first load of the page and allow the client to do all the work.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// /** @type {import('./$types').PageLoad} */ <-- Or this if using +page.js
import type { PageLoad } from './$types';
export const ssr = false;

export const load: PageLoad = async () => {
	console.log('This will run on the server.');

	return {
		person: {
			name: 'Fred',
			email: 'fred@fred.com',
			rnd: Math.floor(Math.random() * 10000)
		}
	};
};

Example API call (client and server)

This example shows an example of calling an API on the client and server side. You’ll want to use this sort of setup if you’re not using a third-party API which requires your private key. For example, let’s say you’ve built a site which is processing financial data, but you’re sourcing that data from a third-party you’re paying for. It’s likely they’ve given you an API key which you don’t want to share. In that case you’ll want to run those calls exclusively on the server.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// /** @type {import('./$types').PageLoad} */ <-- Or this if using +page.js
import type { PageLoad } from './$types';

export const load: PageLoad = async ({ fetch }) => {
	console.log('This will run on the server and the client.');

	let response = await fetch('https://dogapi.dog/api/v2/breeds');
	if (!response.ok) {
		console.log('Error with fetch');
	}

	let breeds: any = await response.json();
	let breedNames: Array<string> = [];

	for (const breed of breeds.data) {
		breedNames.push(breed.attributes.name);
	}

	return { names: breedNames };
};

In this example we use an API for dogs which is so kindly run as free and open. In it we do some processing on the data returned and extract the names only before returning that to our user.

Example API call (server only)

Here we make an API call which is done only on the server. In this case we’ll pretend that the API needs a key and load a key which is privately stored in a .env file at the base of our project folder.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// /** @type {import('./$types').PageServerLoad} */ <-- Or this if using +page.server.js
import type { PageServerLoad } from './$types';
import { PRIVATE_API_KEY } from '$env/static/private';

export const load: PageServerLoad = async ({ fetch }) => {
	console.log('This will run on the server and the client.');
	console.log('Use this if your API has a private key: ', PRIVATE_API_KEY);

	let response = await fetch('https://dogapi.dog/api/v2/breeds');
	if (!response.ok) {
		console.log('Error with fetch');
	}

	let breeds: any = await response.json();
	let breedNames: Array<string> = [];

	for (const breed of breeds.data) {
		breedNames.push(breed.attributes.name);
	}
	return { names: breedNames };
};

To better understand how environment variables work, I’d suggest this article.

Redirects and errors

You can also throw redirects and errors, sending a user to another page or displaying an error.

1
2
3
4
5
6
7
8
9
// /** @type {import('./$types').PageLoad} */ <-- Or this if using +page.server.js
import type { PageLoad } from './$types';
import { error, redirect } from '@sveltejs/kit';

export const load: PageLoad = async () => {
	// throw error(401, 'Not logged it');
	// throw error(404, 'Page does not exist');
	throw redirect(302, '/');
};

In this case I’ve commented out the errors, so uncomment and give them a try to see how they work.

Summary

The load function is a handy way to do processing prior to rendering, and the new division between +page.server.ts and +page.ts makes this more explicit and in my opinion easier to deal with than it was previously.