Reactivity in Svelte
In Svelte 5, create one entry with useHead() and patch it from an $effect() that reads your $state() values.
useHead() when tags must appear in the server response, then use an effect to patch later client-side changes.Svelte Integration
The adapter reads the head instance from Svelte context. Each component can create an entry; the adapter disposes it with onDestroy() in the browser.
Reactive Head Tags
Svelte tracks the state read synchronously inside an effect and reruns it when that state changes. Use two steps:
- Initialize a head entry using
useHead() - Use the returned
patch()function within a Svelte$effectto update when state changes
Basic Pattern
<script lang="ts">
import { useHead } from '@unhead/svelte'
// Reactive state
let title = $state('My Page Title')
// Create a head entry instance
const entry = useHead({
title,
meta: [{ name: 'description', content: `Description for ${title}` }],
})
// Set up the reactive effect
$effect(() => {
entry.patch({
title,
meta: [
{ name: 'description', content: `Description for ${title}` }
]
})
})
function updateTitle() {
title = 'Updated Title'
}
</script>
<button onclick={updateTitle}>Update Title</button>
Entry Lifecycle
Initialize Once, Patch Often
Create the head entry once, outside of the effect, and use patch() within the effect:
// ✅ Good: Create entry once
const entry = useHead()
$effect(() => {
entry.patch({ title })
})
// ❌ Avoid: Creating new entry on each update
$effect(() => {
useHead({ title })
})
Creating the entry outside the effect avoids adding another entry on every update.
Component Cleanup
The adapter disposes the entry from Svelte's onDestroy hook.
Advanced Usage
Multiple Components
When multiple components modify the same head properties, Unhead handles priorities based on mount order (later components have higher priority):
<!-- ComponentA.svelte -->
<script>
import { useHead } from '@unhead/svelte'
let count = $state(0)
const entry = useHead()
$effect(() => {
entry.patch({
title: `Count A: ${count}`
})
})
</script>
<!-- ComponentB.svelte -->
<script>
import { useHead } from '@unhead/svelte'
let count = $state(0)
const entry = useHead()
$effect(() => {
entry.patch({
title: `Count B: ${count}`
})
})
</script>
If both components are mounted, ComponentB wins because it mounted last.
Other Composables
The same reactivity pattern applies to other Unhead composables:
<script>
import { useSeoMeta } from '@unhead/svelte'
let title = $state('My Page')
let description = $state('Page description')
const entry = useSeoMeta()
$effect(() => {
entry.patch({
title,
description
})
})
</script>