Using Unhead with SvelteKit · Unhead

[Unhead Home](https://unhead.unjs.io/ "Home")

- [Docs](https://unhead.unjs.io/docs/svelte/head/guides/get-started/overview)
- [Tools](https://unhead.unjs.io/tools)
- [Learn](https://unhead.unjs.io/learn/guides/what-is-capo)

[Releases](https://unhead.unjs.io/releases)

Search…```k`` /`

[Unhead on GitHub](https://github.com/unjs/unhead)

[User Guides](https://unhead.unjs.io/docs/svelte/head/guides/get-started/overview)

[API](https://unhead.unjs.io/docs/svelte/head/api/get-started/overview)

[Releases](https://unhead.unjs.io/docs/svelte/releases/v3)

Svelte

- [Switch to Svelte](https://unhead.unjs.io/docs/svelte/head/guides/get-started/installation)
- [Switch to TypeScript](https://unhead.unjs.io/docs/typescript/head/guides/get-started/installation)
- [Switch to Vue](https://unhead.unjs.io/docs/vue/head/guides/get-started/installation)
- [Switch to React](https://unhead.unjs.io/docs/react/head/guides/get-started/installation)
- [Switch to Solid.js](https://unhead.unjs.io/docs/solid-js/head/guides/get-started/installation)
- [Switch to Angular](https://unhead.unjs.io/docs/angular/head/guides/get-started/installation)
- [Switch to Nuxt](https://unhead.unjs.io/docs/nuxt/head/guides/get-started/installation)

v3 (stable)

Head

- [Discord Support](https://discord.com/invite/275MBUBvgP)
- [Svelte Playground](https://stackblitz.com/edit/github-ckbygkxk)

- Get Started
  - [Overview](https://unhead.unjs.io/docs/svelte/head/guides/get-started/overview)
  - [Introduction to Unhead](https://unhead.unjs.io/docs/svelte/head/guides/get-started/intro-to-unhead)
  - [Starter Recipes](https://unhead.unjs.io/docs/svelte/head/guides/get-started/starter-recipes)
  - [Installation](https://unhead.unjs.io/docs/svelte/head/guides/get-started/installation)
  - [Upgrade Guide](https://unhead.unjs.io/docs/svelte/head/guides/get-started/migration)
  - [SvelteKit](https://unhead.unjs.io/docs/svelte/head/guides/get-started/sveltekit)
- Core Concepts
  - [Titles & Title Templates](https://unhead.unjs.io/docs/svelte/head/guides/core-concepts/titles)
  - [Tag Sorting & Placement](https://unhead.unjs.io/docs/svelte/head/guides/core-concepts/positions)
  - [Class & Style Attributes](https://unhead.unjs.io/docs/svelte/head/guides/core-concepts/class-attr)
  - [Inline Style & Scripts](https://unhead.unjs.io/docs/svelte/head/guides/core-concepts/inner-content)
  - [Tag Deduplication](https://unhead.unjs.io/docs/svelte/head/guides/core-concepts/handling-duplicates)
  - [DOM Event Handling](https://unhead.unjs.io/docs/svelte/head/guides/core-concepts/dom-event-handling)
  - [Script Loading](https://unhead.unjs.io/docs/svelte/head/guides/core-concepts/loading-scripts)
  - [Reactivity](https://unhead.unjs.io/docs/svelte/head/guides/core-concepts/reactivity)
  - [StreamingNew](https://unhead.unjs.io/docs/svelte/head/guides/core-concepts/streaming)
- Build Plugins
  - [Overview](https://unhead.unjs.io/docs/svelte/head/guides/build-plugins/overview)
  - [Tree-Shaking](https://unhead.unjs.io/docs/svelte/head/guides/build-plugins/tree-shaking)
  - [useSeoMeta Transform](https://unhead.unjs.io/docs/svelte/head/guides/build-plugins/seo-meta-transform)
  - [Minify Transform](https://unhead.unjs.io/docs/svelte/head/guides/build-plugins/minify-transform)
  - [Devtools](https://unhead.unjs.io/docs/svelte/head/guides/build-plugins/devtools)
- Plugins
  - [Template Params](https://unhead.unjs.io/docs/svelte/head/guides/plugins/template-params)
  - [Alias Sorting](https://unhead.unjs.io/docs/svelte/head/guides/plugins/alias-sorting)
  - [Canonical Plugin](https://unhead.unjs.io/docs/svelte/head/guides/plugins/canonical)
  - [Infer SEO Meta](https://unhead.unjs.io/docs/svelte/head/guides/plugins/infer-seo-meta-tags)
  - [Minify](https://unhead.unjs.io/docs/svelte/head/guides/plugins/minify)
  - [Validate](https://unhead.unjs.io/docs/svelte/head/guides/plugins/validate)

Get Started

# Using Unhead with SvelteKit

[Copy for LLMs](https://raw.githubusercontent.com/unjs/unhead/refs/heads/main/docs/0.svelte/head/guides/0.get-started/3.sveltekit.md)

Last updated Apr 5, 2026 by [Harlan Wilton](https://github.com/harlan-zw) in [docs: add SvelteKit integration guide (#677)](https://github.com/unjs/unhead/pull/677).

On this page

- [Introduction](#introduction)
- [Setup](#setup)
- [How the pieces fit together](#how-the-pieces-fit-together)
- [Next Steps](#next-steps)

## [Introduction](#introduction)

[SvelteKit](https://kit.svelte.dev/) provides its own SSR infrastructure that differs from the basic Vite SSR template. This guide covers how to wire up Unhead using SvelteKit's `handle` hook and Svelte context API.

**Quick Start:** Install `@unhead/svelte`, create the head in `hooks.server.ts`, set SSR tags via `locals.unhead.push()` in load functions, provide a client-side head via context in `+layout.svelte`, and use `useHead()` in your components for client-side reactivity.

## [Setup](#setup)

### [1. Install the package](#_1-install-the-package)

pnpm

bun

npm

yarn

bash

`pnpm add @unhead/svelte@next`

### [2. Update app.d.ts](#_2-update-appdts)

Extend SvelteKit's `Locals` interface so the head instance can flow through the request:

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](#_3-create-the-head-in-hooksserverts)

Use the `handle` hook to create a head instance per request and render the managed tags into the HTML response:

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, {
    transformPageChunk: async ({ html }) => {
      return transformHtmlTemplate(unhead, html)
    },
  })

  return response
}
```

`transformHtmlTemplate()` parses the HTML to extract any existing head tags and attributes, pushes them into Unhead, then renders all managed tags back into the document — replacing and deduplicating as needed. It does not rely on static placeholder tokens.

### [4. Set SSR head tags in server load functions](#_4-set-ssr-head-tags-in-server-load-functions)

SvelteKit serialises `load` return values, so the `unhead` instance cannot be passed to components directly. Instead, push SSR head tags from your server load functions using `locals.unhead`:

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:

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](#_5-provide-a-client-side-head-in-layoutsvelte)

After hydration, create a client-side head instance and provide it via Svelte context so that `useHead()` works in components for client-side navigation and reactivity:

src/routes/+layout.svelte

```
<script lang="ts">
  import { setContext } from 'svelte'
  import { browser } from '$app/environment'
  import { UnheadContextKey } from '@unhead/svelte'
  import { createHead } from '@unhead/svelte/client'

  if (browser) {
    const unhead = createHead()
    setContext(UnheadContextKey, unhead)
  }
</script>

<slot />
```

On the server, head tags are managed via `locals.unhead.push()` in load functions. The layout only needs to provide the client-side instance so that `useHead()` calls work after hydration for client-side navigation.

### [6. Use useHead() in your components](#_6-use-usehead-in-your-components)

After hydration, `useHead()` and `useSeoMeta()` are available in any component for client-side head management:

src/routes/+page.svelte

```
<script lang="ts">
  import { useHead } from '@unhead/svelte'

  useHead({
    title: 'My SvelteKit App',
    meta: [
      { name: 'description', content: 'Built with SvelteKit and Unhead' }
    ]
  })
</script>

<h1>Home</h1>
```

For reactive tags driven by page data during client-side navigation:

src/routes/blog/[slug]/+page.svelte

```
<script lang="ts">
  import { useSeoMeta } from '@unhead/svelte'
  import type { PageData } from './$types'

  let { data }: { data: PageData } = $props()

  const entry = useSeoMeta()

  $effect(() => {
    entry.patch({
      title: data.post.title,
      description: data.post.excerpt,
      ogImage: data.post.coverImage,
    })
  })
</script>
```

For initial page loads (SSR), set head tags in `+page.server.ts` via `locals.unhead.push()`. The `useHead()` composable in components handles client-side navigation and reactive updates after hydration.

### [7. Add default tags (optional)](#_7-add-default-tags-optional)

Set site-wide defaults in `hooks.server.ts` using the `init` option:

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({
    init: [
      {
        htmlAttrs: { lang: 'en' },
        title: 'My SvelteKit App',
        titleTemplate: '%s | My Site',
        meta: [
          { name: 'description', content: 'Default site description' }
        ],
      },
    ],
  })
  event.locals.unhead = unhead

  const response = await resolve(event, {
    transformPageChunk: async ({ html }) => {
      return transformHtmlTemplate(unhead, html)
    },
  })

  return response
}
```

## [How the pieces fit together](#how-the-pieces-fit-together)

| File | Role |
| --- | --- |
| `hooks.server.ts` | Creates a per-request head instance, renders tags into HTML via `transformHtmlTemplate()` |
| `+layout.server.ts` / `+page.server.ts` | Push SSR head tags via `locals.unhead.push()` |
| `+layout.svelte` | Provides a client-side head instance via Svelte context |
| `+page.svelte` / any component | Calls `useHead()` / `useSeoMeta()` for client-side reactivity |

On the **server**, `hooks.server.ts` creates the head instance. Server load functions push tags via `locals.unhead`. After rendering, `transformHtmlTemplate()` extracts existing head markup from the HTML, merges it with managed tags, and writes the final head into the response.

On the **client**, the layout creates a new head instance and provides it via context. SvelteKit hydrates the page and subsequent navigation triggers reactive updates through `useHead()` and `$effect()`.

## [Next Steps](#next-steps)

- Use [`useSeoMeta()`](https://unhead.unjs.io/docs/head/api/composables/use-seo-meta) for a flat, type-safe SEO API
- Add [`useSchemaOrg()`](https://unhead.unjs.io/docs/schema-org/api/composables/use-schema-org) for structured data
- Use [`useScript()`](https://unhead.unjs.io/docs/head/api/composables/use-script) for optimized third-party script loading
- Read the [Reactivity](https://unhead.unjs.io/docs/svelte/head/guides/core-concepts/reactivity) guide for reactive head tags

[Edit this page](https://github.com/unjs/unhead/edit/main/docs/0.svelte/head/guides/0.get-started/3.sveltekit.md)

[Markdown For LLMs](https://raw.githubusercontent.com/unjs/unhead/refs/heads/main/docs/0.svelte/head/guides/0.get-started/3.sveltekit.md)

Did this page help you?

[Upgrade Guide Learn how to migrate between Unhead versions for Svelte users.](https://unhead.unjs.io/docs/svelte/head/guides/get-started/migration) [Reactivity Use Svelte 5 $state() and $effect() with useHead() for reactive head tags. Use patch() to update entries.](https://unhead.unjs.io/docs/svelte/head/guides/core-concepts/reactivity)

On this page

- [Introduction](#introduction)
- [Setup](#setup)
- [How the pieces fit together](#how-the-pieces-fit-together)
- [Next Steps](#next-steps)

[GitHub](https://github.com/unjs/unhead) [ Discord](https://discord.com/invite/275MBUBvgP)

[ /llms.txt](https://unhead.unjs.io/llms.txt)

[Part of the UnJS ecosystem](https://unjs.io/)

### Head Management

- [Getting Started](https://unhead.unjs.io/docs/svelte/head/guides/get-started/overview)
- [useHead](https://unhead.unjs.io/docs/svelte/head/api/composables/use-head)
- [useSeoMeta](https://unhead.unjs.io/docs/svelte/head/api/composables/use-seo-meta)
- [useHeadSafe](https://unhead.unjs.io/docs/svelte/head/api/composables/use-head-safe)
- [useScript](https://unhead.unjs.io/docs/svelte/head/api/composables/use-script)

### Schema.org

- [Getting Started](https://unhead.unjs.io/docs/svelte/schema-org/guides/get-started/overview)
- [useSchemaOrg](https://unhead.unjs.io/docs/svelte/schema-org/api/composables/use-schema-org)
- [Nodes](https://unhead.unjs.io/docs/svelte/schema-org/guides/core-concepts/nodes)
- [Recipes](https://unhead.unjs.io/docs/svelte/schema-org/guides/recipes/identity)

### Guides

- [Titles](https://unhead.unjs.io/docs/svelte/head/guides/core-concepts/titles)
- [Streaming SSR](https://unhead.unjs.io/docs/svelte/head/guides/core-concepts/streaming)
- [DOM Events](https://unhead.unjs.io/docs/svelte/head/guides/core-concepts/dom-event-handling)
- [Plugins](https://unhead.unjs.io/docs/svelte/head/guides/plugins/template-params)

### Tools

- [Meta Tag Generator](https://unhead.unjs.io/tools/meta-tag-generator)
- [OG Image Generator](https://unhead.unjs.io/tools/og-image-generator)
- [Schema.org Generator](https://unhead.unjs.io/tools/schema-generator)
- [Capo.js Analyzer](https://unhead.unjs.io/tools/capo-analyzer)

### Articles

- [What is Capo.js?](https://unhead.unjs.io/learn/guides/what-is-capo)

### Research

- [State of <head> in 2026](https://unhead.unjs.io/learn/research/state-of-head-2026)
- [Streaming Head Performance](https://unhead.unjs.io/learn/research/streaming-head-performance)
- [Capo.js Performance Research](https://unhead.unjs.io/learn/research/capo-performance-research)

Copyright © 2025-2026 Harlan Wilton - [MIT License](https://github.com/unjs/unhead/blob/main/license)