Skip to main content

Loading data

Edit this page on GitHub

A +page.svelte or +layout.svelte gets its data from a load function.

If the load function is defined in +page.js or +layout.js it will run both on the server and in the browser. If it's instead defined in +page.server.js or +layout.server.js it will only run on the server, in which case it can (for example) make database calls and access private environment variables, but can only return data that can be serialized with devalue. In both cases, the return value (if there is one) must be an object.

src/routes/+page.js
ts
/** @type {import('./$types').PageLoad} */
export function load(event) {
return {
some: 'data'
};
}
src/routes/+page.ts
ts
import type { PageLoad } from './$types';
 
export const load: PageLoad = (event) => {
return {
some: 'data'
};
}

Input propertiespermalink

The argument to a load function is a LoadEvent (or, for server-only load functions, a ServerLoadEvent which inherits clientAddress, cookies, locals, platform and request from RequestEvent). All events have the following properties:

datapermalink

Very rarely, you might need both a +page.js and a +page.server.js (or the +layout equivalent). In these cases, the data for +page.svelte comes from +page.js, which in turn receives data from the server:

src/routes/my-route/+page.server.js
ts
/** @type {import('./$types').PageServerLoad} */
export function load() {
return {
a: 1
};
}
src/routes/my-route/+page.server.ts
ts
import type { PageServerLoad } from './$types';
 
export const load: PageServerLoad = () => {
return {
a: 1
};
}
src/routes/my-route/+page.js
ts
/** @type {import('./$types').PageLoad} */
export function load({ data }) {
return {
b: data.a * 2
};
}
src/routes/my-route/+page.ts
ts
import type { PageLoad } from './$types';
 
export const load: PageLoad = ({ data }) => {
return {
b: data.a * 2
};
}
src/routes/my-route/+page.svelte
<script>
  /** @type {import('./$types').PageData} */  export let data;

  console.log(data.a); // `undefined`, it wasn't passed through in +page.js
  console.log(data.b); // `2`
</script>
src/routes/my-route/+page.svelte
<script lang="ts">
  import type { PageData } from './$types';

  export let data: PageData;

  console.log(data.a); // `undefined`, it wasn't passed through in +page.js
  console.log(data.b); // `2`
</script>

In other words +page.server.js passes data along to +page.js, which passes data along to +page.svelte.

paramspermalink

params is derived from url.pathname and the route filename.

For a route filename example like src/routes/a/[b]/[...c] and a url.pathname of /a/x/y/z, the params object would look like this:

{
  "b": "x",
  "c": "y/z"
}

routeIdpermalink

The name of the current route directory, relative to src/routes:

src/routes/blog/[slug]/+page.js
ts
/** @type {import('./$types').PageLoad} */
export function load({ routeId }) {
console.log(routeId); // 'blog/[slug]'
}
src/routes/blog/[slug]/+page.ts
ts
import type { PageLoad } from './$types';
 
export const load: PageLoad = ({ routeId }) => {
console.log(routeId); // 'blog/[slug]'
}

urlpermalink

An instance of URL, containing properties like the origin, hostname, pathname and searchParams (which contains the parsed query string as a URLSearchParams object). url.hash cannot be accessed during load, since it is unavailable on the server.

In some environments this is derived from request headers during server-side rendering. If you're using adapter-node, for example, you may need to configure the adapter in order for the URL to be correct.

Input methodspermalink

LoadEvent also has the following methods:

dependspermalink

This function declares that the load function has a dependency on one or more URLs or custom identifiers, which can subsequently be used with invalidate() to cause load to rerun.

Most of the time you won't need this, as fetch calls depends on your behalf — it's only necessary if you're using a custom API client that bypasses fetch.

URLs can be absolute or relative to the page being loaded, and must be encoded.

Custom identifiers have to be prefixed with one or more lowercase letters followed by a colon to conform to the URI specification.

The following example shows how to use depends to register a dependency on the URLs to a custom API client as well as a custom identifier, which is invalidated after a button click, making the load function rerun.

src/routes/+page.js
ts
import * as api from '$lib/api';
 
/** @type {import('./$types').PageLoad} */
export async function load({ depends }) {
depends(
`${api.base}/foo`,
`${api.base}/bar`,
'my-stuff:foo'
);
 
return {
foo: api.client.get('/foo'),
bar: api.client.get('/bar')
};
}
src/routes/+page.ts
ts
import * as api from '$lib/api';
import type { PageLoad } from './$types';
 
export const load: PageLoad = async ({ depends }) => {
depends(
`${api.base}/foo`,
`${api.base}/bar`,
'my-stuff:foo'
);
 
return {
foo: api.client.get('/foo'),
bar: api.client.get('/bar')
};
}
src/routes/+page.svelte
<script>
  import { invalidate } from '$app/navigation';
  /** @type {import('./$types').PageData} */  export let data;

  const pageRefresh = async () => {
    await invalidate('my-stuff:foo');
  }
</script>

<p>{data.foo}<p>
<p>{data.bar}</p>
<button on:click={pageRefresh}>Refresh my stuff</button>
src/routes/+page.svelte
<script lang="ts">
  import { invalidate } from '$app/navigation';

  import type { PageData } from './$types';

  export let data: PageData;

  const pageRefresh = async () => {
    await invalidate('my-stuff:foo');
  }
</script>

<p>{data.foo}<p>
<p>{data.bar}</p>
<button on:click={pageRefresh}>Refresh my stuff</button>

fetchpermalink

fetch is equivalent to the native fetch web API, with a few additional features:

  • it can be used to make credentialed requests on the server, as it inherits the cookie and authorization headers for the page request
  • it can make relative requests on the server (ordinarily, fetch requires a URL with an origin when used in a server context)
  • internal requests (e.g. for +server.js routes) go direct to the handler function when running on the server, without the overhead of an HTTP call
  • during server-side rendering, the response will be captured and inlined into the rendered HTML. Note that headers will not be serialized, unless explicitly included via filterSerializedResponseHeaders
  • during hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request

Cookies will only be passed through if the target host is the same as the SvelteKit application or a more specific subdomain of it.

parentpermalink

await parent() returns data from parent layout load functions. In +page.server.js or +layout.server.js it will return data from load functions in parent +layout.server.js files:

src/routes/+layout.server.js
ts
/** @type {import('./$types').LayoutServerLoad} */
export function load() {
return { a: 1 };
}
src/routes/+layout.server.ts
ts
import type { LayoutServerLoad } from './$types';
 
export const load: LayoutServerLoad = () => {
return { a: 1 };
}
src/routes/foo/+layout.server.js
ts
/** @type {import('./$types').LayoutServerLoad} */
export async function load({ parent }) {
const { a } = await parent();
console.log(a); // `1`
 
return { b: 2 };
}
src/routes/foo/+layout.server.ts
ts
import type { LayoutServerLoad } from './$types';
 
export const load: LayoutServerLoad = async ({ parent }) => {
const { a } = await parent();
console.log(a); // `1`
 
return { b: 2 };
}
src/routes/foo/+page.server.js
ts
/** @type {import('./$types').PageServerLoad} */
export async function load({ parent }) {
const { a, b } = await parent();
console.log(a, b); // `1`, `2`
 
return { c: 3 };
}
src/routes/foo/+page.server.ts
ts
import type { PageServerLoad } from './$types';
 
export const load: PageServerLoad = async ({ parent }) => {
const { a, b } = await parent();
console.log(a, b); // `1`, `2`
 
return { c: 3 };
}

In +page.js or +layout.js it will return data from load functions in parent +layout.js files. Implicitly, a missing +layout.js is treated as a ({ data }) => data function, meaning that it will also return data from parent +layout.server.js files.

Be careful not to introduce accidental waterfalls when using await parent(). If for example you only want to merge parent data into the returned output, call it after fetching your other data.

src/routes/foo/+page.server.js
// @filename: $types.d.ts
export type PageServerLoad = import('@sveltejs/kit').Load<{}, null, { a: number, b: number }>;

// @filename: index.js
// ---cut---
/** @type {import('./$types').PageServerLoad} */
export async function load({ parent, fetch }) {
	const parentData = await parent();
  const data = await fetch('./some-api');
	const parentData = await parent();

  return {
    ...data
    meta: { ...parentData.meta, ...data.meta }
  };
}

setHeaderspermalink

If you need to set headers for the response, you can do so using the setHeaders method. This is useful if you want the page to be cached, for example:

src/routes/blog/+page.js
ts
/** @type {import('./$types').PageLoad} */
export async function load({ fetch, setHeaders }) {
const url = `https://cms.example.com/articles.json`;
const response = await fetch(url);
 
setHeaders({
age: response.headers.get('age'),
'cache-control': response.headers.get('cache-control')
});
 
return response.json();
}
src/routes/blog/+page.ts
ts
import type { PageLoad } from './$types';
 
export const load: PageLoad = async ({ fetch, setHeaders }) => {
const url = `https://cms.example.com/articles.json`;
const response = await fetch(url);
 
setHeaders({
age: response.headers.get('age'),
'cache-control': response.headers.get('cache-control')
});
 
return response.json();
}

setHeaders has no effect when a load function runs in the browser.

Setting the same header multiple times (even in separate load functions) is an error — you can only set a given header once.

You cannot add a set-cookie header with setHeaders — use the cookies API in a server-only load function instead.

Outputpermalink

The returned data, if any, must be an object of values. For a server-only load function, these values must be serializable with devalue. Top-level promises will be awaited, which makes it easy to return multiple promises without creating a waterfall:

ts
/** @type {import('./$types').PageLoad} */
export function load() {
return {
a: Promise.resolve('a'),
b: Promise.resolve('b'),
c: {
value: Promise.resolve('c')
}
};
}
<script>
  /** @type {import('./$types').PageData} */  export let data;

  console.log(data.a); // 'a'
  console.log(data.b); // 'b'
  console.log(data.c.value); // `Promise {...}`
</script>

Errorspermalink

If an error is thrown during load, the nearest +error.svelte will be rendered. For expected errors, use the error helper from @sveltejs/kit to specify the HTTP status code and an optional message:

src/routes/admin/+layout.server.js
ts
import { error } from '@sveltejs/kit';
 
/** @type {import('./$types').LayoutServerLoad} */
export function load({ locals }) {
if (!locals.user) {
throw error(401, 'not logged in');
}
 
if (!locals.user.isAdmin) {
throw error(403, 'not an admin');
}
}
src/routes/admin/+layout.server.ts
ts
import { error } from '@sveltejs/kit';
import type { LayoutServerLoad } from './$types';
 
export const load: LayoutServerLoad = ({ locals }) => {
if (!locals.user) {
throw error(401, 'not logged in');
}
 
if (!locals.user.isAdmin) {
throw error(403, 'not an admin');
}
}

If an unexpected error is thrown, SvelteKit will invoke handleError and treat it as a 500 Internal Error.

Redirectspermalink

To redirect users, use the redirect helper from @sveltejs/kit to specify the location to which they should be redirected alongside a 3xx status code.

src/routes/admin/+layout.server.js
import { error } from '@sveltejs/kit';
import { error, redirect } from '@sveltejs/kit';

/** @type {import('./$types').LayoutServerLoad} */
export function load({ locals }) {
  if (!locals.user) {
		throw error(401, 'not logged in');
		throw redirect(307, '/login');
  }

  if (!locals.user.isAdmin) {
    throw error(403, 'not an admin');
  }
}

Invalidationpermalink

SvelteKit tracks the dependencies of each load function to avoid re-running it unnecessarily during navigation. For example, a load function in a root +layout.js doesn't need to re-run when you navigate from one page to another unless it references url or a member of params that changed since the last navigation.

A load function will re-run in the following situations:

  • It references a property of params whose value has changed
  • It references a property of url (such as url.pathname or url.search) whose value has changed
  • It calls await parent() and a parent load function re-ran
  • It declared a dependency on a specific URL via fetch or depends, and that URL was marked invalid with invalidate(url)
  • All active load functions were forcibly re-run with invalidateAll()

If a load function is triggered to re-run, the page will not remount — instead, it will update with the new data. This means that components' internal state is preserved. If this isn't want you want, you can reset whatever you need to reset inside an afterNavigate callback, and/or wrap your component in a {#key ...} block.

Shared statepermalink

In many server environments, a single instance of your app will serve multiple users. For that reason, per-request state must not be stored in shared variables outside your load functions, but should instead be stored in event.locals. Similarly, per-user state must not be stored in global variables, but should instead make use of $page.data (which contains the combined data of all load functions) or use Svelte's context feature to create scoped state.

We stand with Ukraine. Donate → We stand with Ukraine. Petition your leaders. Show your support.