Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 45 additions & 38 deletions documentation/docs/20-core-concepts/20-load.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ export async function load({ params, parent }) {

## Errors

If an error is thrown during `load`, the nearest [`+error.svelte`](routing#error) will be rendered. For _expected_ errors, use the `error` helper from `@sveltejs/kit` to specify the HTTP status code and an optional message:
If an error is thrown during `load`, the nearest [`+error.svelte`](routing#error) will be rendered. For [_expected_](/docs/errors#expected-errors) errors, use the `error` helper from `@sveltejs/kit` to specify the HTTP status code and an optional message:

```js
/// file: src/routes/admin/+layout.server.js
Expand Down Expand Up @@ -404,11 +404,15 @@ export function load({ locals }) {
}
```

If an _unexpected_ error is thrown, SvelteKit will invoke [`handleError`](hooks#shared-hooks-handleerror) and treat it as a 500 Internal Error.
Calling `error(...)` will throw an exception, making it easy to stop execution from inside helper functions.

If an [_unexpected_](/docs/errors#unexpected-errors) error is thrown, SvelteKit will invoke [`handleError`](hooks#shared-hooks-handleerror) and treat it as a 500 Internal Error.

> [In SvelteKit 1.x](migrating-to-sveltekit-2#redirect-and-error-are-no-longer-thrown-by-you) you had to `throw` the error yourself

## Redirects

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.
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. Like `error(...)`, calling `redirect(...)` will throw an exception, making it easy to stop execution from inside helper functions.

```js
/// file: src/routes/user/+layout.server.js
Expand Down Expand Up @@ -437,53 +441,56 @@ export function load({ locals }) {

In the browser, you can also navigate programmatically outside of a `load` function using [`goto`](modules#$app-navigation-goto) from [`$app.navigation`](modules#$app-navigation).

> [In SvelteKit 1.x](migrating-to-sveltekit-2#redirect-and-error-are-no-longer-thrown-by-you) you had to `throw` the `redirect` yourself

## Streaming with promises

Promises at the _top level_ of the returned object will be awaited, making it easy to return multiple promises without creating a waterfall. When using a server `load`, _nested_ promises will be streamed to the browser as they resolve. This is useful if you have slow, non-essential data, since you can start rendering the page before all the data is available:
When using a server `load`, promises will be streamed to the browser as they resolve. This is useful if you have slow, non-essential data, since you can start rendering the page before all the data is available:

```js
/// file: src/routes/+page.server.js
/// file: src/routes/blog/[slug]/+page.server.js
// @filename: ambient.d.ts
declare global {
const loadPost: (slug: string) => Promise<{ title: string, content: string }>;
const loadComments: (slug: string) => Promise<{ content: string }>;
}

export {};

// @filename: index.js
// ---cut---
/** @type {import('./$types').PageServerLoad} */
export function load() {
export async function load({ params }) {
return {
one: Promise.resolve(1),
two: Promise.resolve(2),
streamed: {
three: new Promise((fulfil) => {
setTimeout(() => {
fulfil(3)
}, 1000);
})
}
// make sure the `await` happens at the end, otherwise we
// can't start loading comments until we've loaded the post
comments: loadComments(params.slug),
post: await loadPost(params.slug)
};
}
```

This is useful for creating skeleton loading states, for example:

```svelte
<!--- file: src/routes/+page.svelte --->
<!--- file: src/routes/blog/[slug]/+page.svelte --->
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>

<p>
one: {data.one}
</p>
<p>
two: {data.two}
</p>
<p>
three:
{#await data.streamed.three}
Loading...
{:then value}
{value}
{:catch error}
{error.message}
{/await}
</p>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>

{#await data.comments}
Loading comments...
{:then comments}
{#each comments as comment}
<p>{comment.content}</p>
{/each}
{:catch error}
<p>error loading comments: {error.message}</p>
{/await}
```

When streaming data, be careful to handle promise rejections correctly. More specifically, the server could crash with an "unhandled promise rejection" error if a lazy-loaded promise fails before rendering starts (at which point it's caught) and isn't handling the error in some way. When using SvelteKit's `fetch` directly in the `load` function, SvelteKit will handle this case for you. For other promises, it is enough to attach a noop-`catch` to the promise to mark it as handled.
Expand All @@ -496,21 +503,21 @@ export function load({ fetch }) {
ok_manual.catch(() => {});

return {
streamed: {
ok_manual,
ok_fetch: fetch('/fetch/that/could/fail'),
dangerous_unhandled: Promise.reject()
}
ok_manual,
ok_fetch: fetch('/fetch/that/could/fail'),
dangerous_unhandled: Promise.reject()
};
}
```

> On platforms that do not support streaming, such as AWS Lambda, responses will be buffered. This means the page will only render once all promises resolve. If you are using a proxy (e.g. NGINX), make sure it does not buffer responses from the proxied server.

> Streaming data will only work when JavaScript is enabled. You should avoid returning nested promises from a universal `load` function if the page is server rendered, as these are _not_ streamed — instead, the promise is recreated when the function reruns in the browser.
> Streaming data will only work when JavaScript is enabled. You should avoid returning promises from a universal `load` function if the page is server rendered, as these are _not_ streamed — instead, the promise is recreated when the function reruns in the browser.

> The headers and status code of a response cannot be changed once the response has started streaming, therefore you cannot `setHeaders` or throw redirects inside a streamed promise.

> [In SvelteKit 1.x](migrating-to-sveltekit-2#top-level-promises-are-no-longer-awaited) top-level promises were automatically awaited, only nested promises were streamed.

## Parallel loading

When rendering (or navigating to) a page, SvelteKit runs all `load` functions concurrently, avoiding a waterfall of requests. During client-side navigation, the result of calling multiple server `load` functions are grouped into a single response. Once all `load` functions have returned, the page is rendered.
Expand Down
4 changes: 3 additions & 1 deletion documentation/docs/30-advanced/25-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export async function load({ params }) {
}
```

This tells SvelteKit to set the response status code to 404 and render an [`+error.svelte`](routing#error) component, where `$page.error` is the object provided as the second argument to `error(...)`.
This throws an exception that SvelteKit catches, causing it to set the response status code to 404 and render an [`+error.svelte`](routing#error) component, where `$page.error` is the object provided as the second argument to `error(...)`.

```svelte
<!--- file: src/routes/+error.svelte --->
Expand All @@ -67,6 +67,8 @@ error(404, {
+error(404, 'Not found');
```

> [In SvelteKit 1.x](migrating-to-sveltekit-2#redirect-and-error-are-no-longer-thrown-by-you) you had to `throw` the `error` yourself

## Unexpected errors

An _unexpected_ error is any other exception that occurs while handling a request. Since these can contain sensitive information, unexpected error messages and stack traces are not exposed to users.
Expand Down