Svelte logo

SvelteKit by Example: Errors & Redirects

Some requests can't render the requested page. Use error for an expected failure, or redirect to send the visitor somewhere else.

In this example, we'll handle a missing todo and redirect after creating one.

We'll use the public demonstration database helpers from the API endpoints example. These routes need a server runtime and must not be prerendered. For private todos, authenticate and check ownership in the load and action too.

getTodo(id) is our application helper. It returns a todo with id, task, and completed, or null when it doesn't exist.

error sets a status and stops loading. It throws for you; don't catch it or write throw error(...).

src/routes/todos/[id]/+page.server.js
import { getTodo } from '#lib/server/db.js';
import { error } from '@sveltejs/kit';

export async function load({ params }) {
  const todo = await getTodo(params.id);





  if (!todo) error(404, 'Todo not found');

  return { todo };
}

If a parent enables prerendering, override it for this dynamic page.

src/routes/todos/[id]/+page.js
export const prerender = false;

A successful load displays the todo as usual.

src/routes/todos/[id]/+page.svelte
<script>
  let { data } = $props();
</script>

<h1>{data.todo.task}</h1>
<p>{data.todo.completed ? 'Done' : 'To do'}</p>

SvelteKit renders the nearest error boundary for a failed page load. Read its status and message from page, not component props.

src/routes/todos/+error.svelte
<script>
  import { page } from '$app/state';
</script>

<h1>{page.status}</h1>
<p>{page.error?.message ?? 'Something went wrong'}</p>
<a href="/todos">Back to todos</a>

Errors from a layout load use a boundary above that layout, not one beside it. Add src/routes/+error.svelte for a site-wide fallback, including unmatched URLs. Endpoint errors don't render +error.svelte; callers must check the response.

Unexpected exceptions become 500 errors. SvelteKit hides their details behind a generic message; use a server handleError hook for logging rather than exposing database errors to visitors.

Redirect After Submission

This is a separate creation page. As in the form actions example, return fail for validation errors so the visitor can correct the form.

After saving, redirect to the new todo. 303 makes the next request a GET instead of repeating the POST. redirect throws for you; don't catch it.

src/routes/todos/new/+page.server.js
import { createTodo } from '#lib/server/db.js';
import { fail, redirect } from '@sveltejs/kit';

export const actions = {
  default: async ({ request }) => {
    const fields = await request.formData();
    const value = fields.get('task');
    const task = typeof value === 'string' ? value.trim() : '';
    if (!task || task.length > 200) {
      return fail(400, { task, message: 'Enter a task of 1–200 characters.' });
    }

    const todo = await createTodo(task);


    redirect(303, `/todos/${encodeURIComponent(todo.id)}`);
  },
};

Actions cannot be prerendered. Override any parent setting on the creation page too.

src/routes/todos/new/+page.js
export const prerender = false;

The form works without JavaScript. enhance also follows the redirect after a successful submission in the browser.

src/routes/todos/new/+page.svelte
<script>
  import { enhance } from '$app/forms';
  let { form } = $props();
</script>




<form method="POST" use:enhance>
  <label for="task">Todo</label>
  <input id="task" name="task" value={form?.task ?? ''} maxlength="200" required>
  <button>Add todo</button>
</form>

{#if form?.message}
  <p>{form.message}</p>
{/if}