Quick Answer: In Svelte 5, use $state() with useHead() inside $effect(). In Svelte 4, use writable stores. Head tags update automatically when state changes.
Unhead integrates with Svelte's reactivity system through the Svelte context API. This guide explains how to effectively manage reactive head tags in your Svelte applications.
Unhead registers itself in the Svelte context, making head management available throughout your component tree. Each component can create head entries that automatically clean up when the component is destroyed.
Svelte doesn't support reactivity outside of components. To create reactive head tags, you need to:
useHead()patch() function within a Svelte $effect to update when state changes<script lang="ts">
import { useHead } from '@unhead/svelte'
// Reactive state
let title = $state('My Page Title')
// Create a head entry instance
const entry = useHead()
// Set up the reactive effect
$effect(() => {
entry.patch({
title,
meta: [
{ name: 'description', content: `Description for ${title}` }
]
})
})
function updateTitle() {
title = 'Updated Title'
}
</script>
<button on:click={updateTitle}>Update Title</button>
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 })
})
This pattern prevents unnecessary memory allocations and ensures proper cleanup.
Yes. Unhead automatically handles cleanup when a component is unmounted. The onDestroy lifecycle hook is used internally to dispose of head entries when the component is removed from the DOM.
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>
In this example, ComponentB's title will be shown if both components are mounted, as it was mounted last.
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>
Unhead in Svelte works by:
tick() for DOM updatesonDestroy for automatic cleanup$effect to track state changes and update head entriesThis integration ensures that your head tags stay in sync with your component state while maintaining performance.