---
title: Using Unhead with SvelteKit
description: 'Set up Unhead in SvelteKit using hooks.server.ts, server load functions, and +layout.svelte.'
navigation:
title: 'SvelteKit'
---
[SvelteKit](https://svelte.dev/docs/kit) applications need separate Unhead instances for server rendering and client-side navigation. This guide connects them through SvelteKit's [`handle` hook](https://svelte.dev/docs/kit/hooks#Server-hooks-handle), `locals`, and Svelte context.
## Setup
### 1. Install the package
:ModuleInstall{name="@unhead/svelte@next"}
### 2. Update app.d.ts
Extend SvelteKit's `Locals` interface so the head instance can flow through the request:
```ts [src/app.d.ts]
import type { Unhead } from '@unhead/svelte/server'
declare global {
namespace App {
interface Locals {
unhead: Unhead
}
}
}
export {}
```
### 3. Create the head in hooks.server.ts
Use the `handle` hook to create a head instance per request and render the managed tags into the HTML response:
```ts [src/hooks.server.ts]
import { createHead, transformHtmlTemplate } from '@unhead/svelte/server'
import type { Handle } from '@sveltejs/kit'
export const handle: Handle = async ({ event, resolve }) => {
const unhead = createHead()
event.locals.unhead = unhead
const response = await resolve(event)
if (!response.headers.get('content-type')?.includes('text/html'))
return response
const html = await response.text()
const transformed = transformHtmlTemplate(unhead, html)
const headers = new Headers(response.headers)
headers.delete('content-length')
return new Response(transformed, {
headers,
status: response.status,
statusText: response.statusText,
})
}
```
`transformHtmlTemplate()` needs the complete HTML document, so this example buffers HTML responses instead of using SvelteKit's chunk transform. It extracts existing head tags and attributes, merges the managed tags, and writes the result back into the document. Non-HTML responses pass through unchanged.
### 4. Set SSR head tags in server load functions
SvelteKit serializes `load` return values, so you cannot pass the `unhead` instance directly to components. Push SSR head tags from server load functions through `locals.unhead`:
```ts [src/routes/+layout.server.ts]
import type { LayoutServerLoad } from './$types'
export const load: LayoutServerLoad = async ({ locals }) => {
// Set site-wide SSR head tags
locals.unhead.push({
htmlAttrs: { lang: 'en' },
titleTemplate: '%s | My Site',
})
}
```
Per-page SSR head tags work the same way:
```ts [src/routes/blog/[slug]/+page.server.ts]
import type { PageServerLoad } from './$types'
export const load: PageServerLoad = async ({ locals, params }) => {
const post = await getPost(params.slug)
// Push page-specific head tags for SSR
locals.unhead.push({
title: post.title,
meta: [
{ name: 'description', content: post.excerpt },
{ property: 'og:image', content: post.coverImage },
],
})
return { post }
}
```
These tags are rendered into the HTML by `transformHtmlTemplate()` in `hooks.server.ts` before the response is sent.
### 5. Provide a client-side head in +layout.svelte
Create a client-side head instance in the root layout and provide it through Svelte context. Components can then call `useHead()` during client-side navigation:
```svelte [src/routes/+layout.svelte]