Skip to main content
Hooks are app-wide functions you declare that SvelteKit calls in response to specific events, giving you fine-grained control over the framework’s behavior.

Overview

There are three hooks files, all optional:

src/hooks.server.js

Server-only hooks

src/hooks.client.js

Client-only hooks

src/hooks.js

Universal hooks (both)
Code in these modules runs when the application starts up, making them useful for initializing database clients and other setup tasks.

Server hooks

The following hooks can be added to src/hooks.server.js:

handle

This function runs every time the SvelteKit server receives a request and determines the response. It receives an event object and a resolve function that renders the route.
/// file: src/hooks.server.js
export async function handle({ event, resolve }) {
	if (event.url.pathname.startsWith('/custom')) {
		return new Response('custom response');
	}

	const response = await resolve(event);
	return response;
}
Requests for static assets and prerendered pages are not handled by SvelteKit.

Advanced resolve options

The resolve function accepts a second parameter with the following options:
transformPageChunk
function
Applies custom transforms to HTML chunks. Useful for search-and-replace operations.
transformPageChunk: ({ html }) => html.replace('old', 'new')
filterSerializedResponseHeaders
function
Determines which headers are included in serialized responses when a load function uses fetch.
filterSerializedResponseHeaders: (name) => name.startsWith('x-')
preload
function
Determines which files are added to the <head> tag for preloading.
preload: ({ type, path }) => type === 'js' || path.includes('/important/')
export async function handle({ event, resolve }) {
	const response = await resolve(event, {
		transformPageChunk: ({ html }) => html.replace('old', 'new'),
		filterSerializedResponseHeaders: (name) => name.startsWith('x-'),
		preload: ({ type, path }) => type === 'js' || path.includes('/important/')
	});

	return response;
}

handleFetch

Modify or replace fetch requests that run on the server during SSR or prerendering.
export async function handleFetch({ request, fetch }) {
	if (request.url.startsWith('https://api.yourapp.com/')) {
		// Rewrite API requests to hit internal endpoint
		request = new Request(
			request.url.replace('https://api.yourapp.com/', 'http://localhost:9999/'),
			request
		);
	}

	return fetch(request);
}
Use handleFetch to bypass proxies and load balancers when making internal API requests during SSR.

handleValidationError

Called when a remote function receives an argument that doesn’t match its schema.
export function handleValidationError({ issues }) {
	return {
		message: 'Validation failed',
		code: 'VALIDATION_ERROR'
	};
}
Be thoughtful about what information you expose here, as validation failures often indicate malicious requests.

Shared hooks

These hooks work in both src/hooks.server.js and src/hooks.client.js:

handleError

Called when an unexpected error is thrown during loading or rendering.
1

Log the error

Send errors to a reporting service like Sentry
2

Generate safe representation

Return a sanitized error object that’s safe to show users
3

Add custom properties

Include tracking IDs or other custom fields
import * as Sentry from '@sentry/sveltekit';

Sentry.init({/*...*/})

export async function handleError({ error, event, status, message }) {
	const errorId = crypto.randomUUID();

	Sentry.captureException(error, {
		extra: { event, errorId, status }
	});

	return {
		message: 'Whoops!',
		errorId
	};
}
Make sure that handleError never throws an error itself.

init

Runs once when the server starts or the app initializes in the browser.
import * as db from '$lib/server/database';

export async function init() {
	await db.connect();
}
In the browser, asynchronous work in init will delay hydration. Be mindful of what you put here.

Universal hooks

These hooks run on both server and client (in src/hooks.js):

reroute

Runs before handle and changes how URLs are translated into routes.
const translated = {
	'/en/about': '/en/about',
	'/de/ueber-uns': '/de/about',
	'/fr/a-propos': '/fr/about',
};

export function reroute({ url }) {
	if (url.pathname in translated) {
		return translated[url.pathname];
	}
}
Using reroute will not change the browser’s address bar or the value of event.url.

transport

Allows custom types to be serialized across the server/client boundary.
import { Vector } from '$lib/math';

export const transport = {
	Vector: {
		encode: (value) => value instanceof Vector && [value.x, value.y],
		decode: ([x, y]) => new Vector(x, y)
	}
};

Sequencing multiple hooks

You can use the sequence helper to run multiple handle functions:
import { sequence } from '@sveltejs/kit/hooks';

async function first({ event, resolve }) {
	console.log('first pre-processing');
	const result = await resolve(event);
	console.log('first post-processing');
	return result;
}

async function second({ event, resolve }) {
	console.log('second pre-processing');
	const result = await resolve(event);
	console.log('second post-processing');
	return result;
}

export const handle = sequence(first, second);

Learn more

Follow the interactive tutorial on hooks