useHead() · Unhead

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

- [Docs](https://unhead.unjs.io/docs/typescript/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/typescript/head/guides/get-started/overview)

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

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

TypeScript

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

v3 (stable)

Head

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

- Get Started
  - [Overview](https://unhead.unjs.io/docs/typescript/head/api/get-started/overview)
- Composables
  - [`useHead()`](https://unhead.unjs.io/docs/typescript/head/api/composables/use-head)
  - [`useHeadSafe()`](https://unhead.unjs.io/docs/typescript/head/api/composables/use-head-safe)
  - [`useSeoMeta()`](https://unhead.unjs.io/docs/typescript/head/api/composables/use-seo-meta)
  - [`useScript()`](https://unhead.unjs.io/docs/typescript/head/api/composables/use-script)
- Hooks
  - [entries:updated](https://unhead.unjs.io/docs/typescript/head/api/hooks/entries-updated)
  - [entries:resolve](https://unhead.unjs.io/docs/typescript/head/api/hooks/entries-resolve)
  - [entries:normalize](https://unhead.unjs.io/docs/typescript/head/api/hooks/entries-normalize)
  - [tag:normalise](https://unhead.unjs.io/docs/typescript/head/api/hooks/tag-normalise)
  - [tags:beforeResolve](https://unhead.unjs.io/docs/typescript/head/api/hooks/tags-before-resolve)
  - [tags:resolve](https://unhead.unjs.io/docs/typescript/head/api/hooks/tags-resolve)
  - [tags:afterResolve](https://unhead.unjs.io/docs/typescript/head/api/hooks/tags-after-resolve)
  - [dom:beforeRender](https://unhead.unjs.io/docs/typescript/head/api/hooks/dom-before-render)
  - [ssr:beforeRender](https://unhead.unjs.io/docs/typescript/head/api/hooks/ssr-before-render)
  - [ssr:render](https://unhead.unjs.io/docs/typescript/head/api/hooks/ssr-render)
  - [ssr:rendered](https://unhead.unjs.io/docs/typescript/head/api/hooks/ssr-rendered)
  - [script:updated](https://unhead.unjs.io/docs/typescript/head/api/hooks/script-updated)
- [Plugins](https://unhead.unjs.io/docs/head/api/plugins)

Composables

# useHead()

[Copy for LLMs](https://raw.githubusercontent.com/unjs/unhead/refs/heads/main/docs/head/7.api/composables/0.use-head.md)

Last updated Apr 10, 2026 by [Harlan Wilton](https://github.com/harlan-zw) in [fix: better type narrowing escape hatches for custom rel/type (#735)](https://github.com/unjs/unhead/pull/735).

On this page

- [How It Works](#how-it-works)
- [Reactivity Model](#reactivity-model)
- [API Reference](#api-reference)
- [Input Schema](#input-schema)
- [Options](#options)
- [Synchronizing with DOM Updates](#synchronizing-with-dom-updates)
- [Reactivity](#reactivity)
- [Security Considerations](#security-considerations)
- [TypeScript](#typescript)
- [Common Mistakes](#common-mistakes)
- [Choosing the Right Composable](#choosing-the-right-composable)
- [Common Questions](#common-questions)
- [Advanced Examples](#advanced-examples)
- [Common Use Cases](#common-use-cases)

**Quick Start:**

```
import { useHead } from '@unhead/vue' // or your framework

useHead(unheadInstance, {
  title: 'Page Title',
  meta: [{ name: 'description', content: 'Page description' }]
})
```

The `useHead()` composable is for managing the document head. It provides a type-safe, reactive API to define, update, and remove head elements like title, meta tags, scripts, and more. It's the core composable used across all frameworks in the Unhead ecosystem.

```
import { useHead } from 'unhead'

const entry = useHead({
  title: 'My Page',
})
// update
entry.patch({ title: 'new Title' })
// remove
entry.dispose()
```

### [How It Works](#how-it-works)

The composable works by queuing your input to be resolved when the head is rendered:

1. It registers your head configuration in a queue
2. When the document head is being rendered (client-side or during SSR), all queued entries are:
  - Resolved (including any functions, promises or reactive values)
  - Deduplicated (removing redundant tags) - see [Handling Duplicates](https://unhead.unjs.io/docs/head/guides/core-concepts/handling-duplicates)
  - Sorted (based on tag priority) - see [Tag Positions](https://unhead.unjs.io/docs/head/guides/core-concepts/positions)
  - Merged when appropriate
3. The resolved tags are then rendered to the document head

This queue-based approach enables powerful features like deduplication, async resolution, and priority-based rendering while maintaining optimal performance.

You won't know the final state of the head until the rendering is complete.

### [Reactivity Model](#reactivity-model)

`useHead()` provides reactivity through two main mechanisms:

1. **Framework Integration**: When used with frameworks it automatically integrates with the framework's reactivity system
2. **Manual API**: The returned `ActiveHeadEntry` object with `patch()` and `dispose()` methods lets you manually update or remove head entries

## [API Reference](#api-reference)

```
function useHead(input: UseHeadInput, options?: HeadEntryOptions): ActiveHeadEntry
```

### [Parameters](#parameters)

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `input` | `Head` | Yes | The head configuration object |
| `options` | `HeadEntryOptions` | No | Configuration options for the head entry |

### [Returns](#returns)

```
interface ActiveHeadEntry {
  /**
   * Update the head entry with new values
   */
  patch: (input: Partial<UseHeadInput>) => void
  /**
   * Remove the head entry
   */
  dispose: () => void
}
```

## [Input Schema](#input-schema)

The input object accepts the following properties:

```
interface Head<E extends MergeHead = SchemaAugmentations> {
  // Document title
  title?: string | Promise<string>

  // Title template (function or string with %s placeholder)
  titleTemplate?: string | null | ((title?: string) => string | null)

  // Template parameters for dynamic replacements
  templateParams?: { separator?: string } & Record<string, string | Record<string, string>>

  // HTML tag collections
  base?: Base<E['base']>
  link?: Link<E['link']>[]
  meta?: Meta<E['meta']>[]
  style?: (Style<E['style']> | string)[]
  script?: (Script<E['script']> | string)[]
  noscript?: (Noscript<E['noscript']> | string)[]

  // Element attributes
  htmlAttrs?: HtmlAttributes<E['htmlAttrs']>
  bodyAttrs?: BodyAttributes<E['bodyAttrs']>
}
```

The input is deeply resolved allowing you to provide any value as a function. This can be useful for lazily resolving values when the head tags are being rendered.

Lazy resolving values can improve performance for complex or computed values that aren't needed until the head is actually rendered.

```
import { useHead } from 'unhead'

const title = useMyTitle()
useHead(unheadInstance, {
  // just works
  title: () => 'Dynamic Title',
  meta: [
    () => ({
      name: 'description',
      content: () => \`Description for ${title.value}\`
    }),
  ]
})
```

## [Options](#options)

The `options` parameter allows you to configure the behavior of the head entry:

```
export interface HeadEntryOptions {
  // Whether to process template parameters in the input
  // - Requires the TemplateParams plugin
  processTemplateParams?: boolean

  // Priority of tags for determining render order
  tagPriority?: number | 'critical' | 'high' | 'low' | \`before:${string}\` | \`after:${string}\`

  // Where to position tags in the document
  tagPosition?: 'head' | 'bodyClose' | 'bodyOpen'

  // Callback fired after DOM updates are applied (client-only, ignored during SSR)
  onRendered?: (ctx: { renders: DomRenderTagContext[] }) => void | Promise<void>

  // Custom head instance
  head?: Unhead
}
```

Setting any of these will apply that rule to all tags within the entry. For example if we want to push several meta tags with low priority, we can do:

Learn more about using [Tag Priorities](https://unhead.unjs.io/docs/head/guides/core-concepts/positions) and [Template Parameters](https://unhead.unjs.io/docs/head/guides/plugins/template-params) in their dedicated guides.

```
import { useHead } from 'unhead'

useHead(unheadInstance, {
  meta: [
    { name: 'description', content: 'fallback description' },
    { name: 'author', content: 'fallback author' }
  ]
}, {
  tagPriority: 'low'
})
```

The `tagPriority` option is particularly useful for controlling render order when you have multiple head entries that might contain similar tags.

## [Synchronizing with DOM Updates](#synchronizing-with-dom-updates)

The `onRendered` option lets you run a callback after unhead has finished applying DOM updates. This is useful for synchronising external tools like analytics with the current document head (e.g. reading `document.title` after it has been set).

```
import { useHead } from 'unhead'

useHead(unheadInstance, {
  title: 'My Page',
}, {
  onRendered({ renders }) {
    // document.title is guaranteed to be up-to-date here
    analytics.track('Page View', { title: document.title })
  }
})
```

- The callback fires on **every** DOM render, not just the first.
- It is **ignored during SSR** — only runs on the client.
- The callback is automatically cleaned up when the entry is disposed (including on component unmount in frameworks).
- The `renders` array contains the render context for each tag that was processed.

Works with all composables that accept `HeadEntryOptions` — `useHead()`, `useSeoMeta()`, and `useHeadSafe()`.

## [Reactivity](#reactivity)

### [Automatic Reactivity](#automatic-reactivity)

The `useHead()` composable automatically integrates with your framework's reactivity system:

Vue

React

Solid.js

```
import { useHead } from 'unhead'
import { computed, ref } from 'vue'

const title = ref('Dynamic Title')

useHead(unheadInstance, {
  title,
  meta: [
    { name: 'description', content: computed(() => \`Description for ${title.value}\`) }
  ]
})
```

### [Manual Control](#manual-control)

For more granular control, you can use the returned API:

```
import { useHead } from 'unhead'

// Create the head entry
const headControl = useHead({
  title: 'Initial Title'
})

// Later update specific fields
headControl.patch({
  title: 'Updated Title',
  meta: [
    { name: 'description', content: 'New description' }
  ]
})

// Remove the entry entirely when needed
headControl.dispose()
```

**Use cases for manual control:**

- Updating head after asynchronous data loading
- Conditional changes based on user interactions
- Managing head from global state
- Creating temporary modifications

For framework-specific reactivity details, see the guides for each specific framework.

## [Security Considerations](#security-considerations)

The `useHead()` function applies minimal sanitization to improve developer experience.**Do not** use this function with untrusted or third-party input. It cannot guarantee safety when handling unknown content.

For XSS protection, either:

1. Sanitize your input before passing it to `useHead()`
2. Use the safer alternatives:
  - [useSeoMeta()](https://unhead.unjs.io/docs/head/api/composables/use-seo-meta) for SEO metadata
  - [useHeadSafe()](https://unhead.unjs.io/docs/head/api/composables/use-head-safe) for general head management

## [TypeScript](#typescript)

Import types directly from your framework's package:

```
import type { ActiveHeadEntry, Head, HeadEntryOptions } from '@unhead/vue'

// Type your head input
const headConfig: Head = {
  title: 'My Page',
  meta: [{ name: 'description', content: 'Page description' }]
}

// Type your entry options
const options: HeadEntryOptions = {
  tagPriority: 'high'
}

const entry: ActiveHeadEntry = useHead(headConfig, options)
```

For custom type extensions, augment the `Head` interface:

```
declare module '@unhead/schema' {
  interface Head {
    customProperty?: string
  }
}
```

### [Type Narrowing](#type-narrowing)

`Link` and `Script` use discriminated unions keyed on `rel` and `type`. Known values enforce per-tag required properties at the type level. For non-standard values not covered by the built-in union, use the `defineLink` and `defineScript` helpers, which preserve strict narrowing on known values and fall through to `GenericLink` / `GenericScript` for anything else:

Non-standard Link rel

Custom Script type

```
import { defineLink, useHead } from 'unhead'

useHead(unheadInstance, {
  link: [
    { rel: 'canonical', href: 'https://example.com' }, // known rel, works directly
    { rel: 'me', href: 'https://mastodon.social/@me' }, // known rel, works directly
    defineLink({ rel: 'openid2.provider', href: 'https://example.com/openid' }), // non-standard rel
  ]
})
```

```
import { defineScript, useHead } from 'unhead'

useHead(unheadInstance, {
  script: [
    { src: 'https://example.com/app.js' }, // external script, works directly
    defineScript({ type: 'text/plain', textContent: '...' }), // custom type
  ]
})
```

When building link or script objects outside of `useHead()`, use `as const` on literal values to preserve type narrowing:

```
const link = { rel: 'preload' as const, as: 'font' as const, href: '/font.woff2', crossorigin: 'anonymous' as const }
useHead(unheadInstance, { link: [link] })
```

`useSeoMeta()` is unaffected by type narrowing and remains the simplest path for SEO meta tags.

## [Common Mistakes](#common-mistakes)

### [Using reactive values incorrectly](#using-reactive-values-incorrectly)

```
// ❌ Wrong - loses reactivity
const title = ref('My Title')
useHead(unheadInstance, { title: title.value })

// ✅ Correct - pass the ref directly
useHead(unheadInstance, { title })
```

### [Calling useHead in async code](#calling-usehead-in-async-code)

```
// ❌ Wrong - may execute outside component context
async function loadData() {
  const data = await fetchData()
  useHead({ title: data.title }) // Context may be lost
}

// ✅ Correct - set up head first, update reactively
const data = ref(null)
useHead(unheadInstance, { title: () => data.value?.title ?? 'Loading...' })
async function loadData() {
  data.value = await fetchData()
}
```

### [Forgetting to dispose manual entries](#forgetting-to-dispose-manual-entries)

```
// ❌ Memory leak if called multiple times
function showModal() {
  useHead({ title: 'Modal Open' })
}

// ✅ Store and dispose when done
let modalHead: ActiveHeadEntry | null = null
function showModal() {
  modalHead = useHead({ title: 'Modal Open' })
}
function hideModal() {
  modalHead?.dispose()
  modalHead = null
}
```

## [Choosing the Right Composable](#choosing-the-right-composable)

| Composable | Use When |
| --- | --- |
| `useHead()` | General head management, scripts, links, full control |
| `useSeoMeta()` | SEO meta tags only - simpler API, type-safe keys |
| `useHeadSafe()` | Working with untrusted/user-provided input |
| `useScript()` | Loading third-party scripts with lifecycle control |

Start with `useSeoMeta()` for SEO tags - it's simpler and prevents mistakes. Use `useHead()` when you need scripts, links, or advanced features.

## [Common Questions](#common-questions)

### [How do I update the title dynamically?](#how-do-i-update-the-title-dynamically)

Use a reactive value or the `patch()` method:

```
const entry = useHead({ title: 'Initial' })
entry.patch({ title: 'Updated Title' })
```

### [How do I remove head tags?](#how-do-i-remove-head-tags)

Call `dispose()` on the returned entry:

```
const entry = useHead({ title: 'Temporary' })
entry.dispose() // removes all tags from this entry
```

## [Advanced Examples](#advanced-examples)

### [Title Template](#title-template)

```
import { useHead } from 'unhead'

useHead(unheadInstance, {
  titleTemplate: title => \`${title} - My Site\`,
  title: 'Home Page'
})
// Results in: "Home Page - My Site"
```

For more details on title templates, see the [Titles guide](https://unhead.unjs.io/docs/head/guides/core-concepts/titles).

### [Combining Multiple Head Entries](#combining-multiple-head-entries)

```
import { useHead } from 'unhead'

// Global site defaults
useHead(unheadInstance, {
  titleTemplate: '%s | My Website',
  meta: [
    { name: 'og:site_name', content: 'My Website' }
  ]
})

// Page-specific entries (will be merged with globals)
useHead(unheadInstance, {
  title: 'Product Page',
  meta: [
    { name: 'description', content: 'This product is amazing' }
  ]
})
```

This pattern is commonly used to implement layouts with defaults and page-specific overrides.

### [Async Data Loading](#async-data-loading)

Vue

React

Solid.js

```
import { useHead } from 'unhead'
import { computed, ref } from 'vue'

const data = ref(null)
const loading = ref(true)

useHead(unheadInstance, {
  title: computed(() => data.value
    ? \`${data.value.name} - Product\`
    : loading.value
      ? 'Loading...'
      : 'Product Not Found')
})

async function fetchProduct(id) {
  loading.value = true
  data.value = await api.getProduct(id)
  loading.value = false
}
```

This pattern works well with data fetching libraries and state management solutions.

### [Priority-Based Tag Ordering](#priority-based-tag-ordering)

```
import { useHead } from 'unhead'

// Critical meta tags (early in <head>)
useHead(unheadInstance, {
  meta: [
    { charset: 'utf-8' },
    { name: 'viewport', content: 'width=device-width, initial-scale=1' }
  ]
}, { tagPriority: 'critical' })

// Default priority tags (middle of <head>)
useHead(unheadInstance, {
  meta: [
    { name: 'description', content: 'My website description' }
  ]
})

// Low priority tags (end of <head>)
useHead(unheadInstance, {
  meta: [
    { name: 'author', content: 'Jane Doe' }
  ]
}, { tagPriority: 'low' })
```

## [Common Use Cases](#common-use-cases)

Here are some common use cases for `useHead()`:

- Setting page-specific metadata for SEO (consider using [useSeoMeta()](https://unhead.unjs.io/docs/head/api/composables/use-seo-meta) for a more convenient API)
- Managing document title and favicon (see [Titles guide](https://unhead.unjs.io/docs/head/guides/core-concepts/titles))
- Adding external scripts and stylesheets (consider using [useScript()](https://unhead.unjs.io/docs/head/api/composables/use-script) for scripts)
- Setting Open Graph and Twitter card tags

For ready-to-use implementations of common patterns, see our [Starter Recipes](https://unhead.unjs.io/docs/head/guides/get-started/starter-recipes).

[Edit this page](https://github.com/unjs/unhead/edit/main/docs/head/7.api/composables/0.use-head.md)

[Markdown For LLMs](https://raw.githubusercontent.com/unjs/unhead/refs/heads/main/docs/head/7.api/composables/0.use-head.md)

Did this page help you?

[Overview Unhead API reference for useHead(), useSeoMeta(), useScript() composables and DOM/SSR rendering hooks. Full TypeScript support.](https://unhead.unjs.io/docs/head/api/get-started/overview) [useHeadSafe() Safely manage head tags with XSS protection using useHeadSafe(). Sanitize untrusted user input for titles, meta tags, and other head elements.](https://unhead.unjs.io/docs/head/api/composables/use-head-safe)

On this page

- [How It Works](#how-it-works)
- [Reactivity Model](#reactivity-model)
- [API Reference](#api-reference)
- [Input Schema](#input-schema)
- [Options](#options)
- [Synchronizing with DOM Updates](#synchronizing-with-dom-updates)
- [Reactivity](#reactivity)
- [Security Considerations](#security-considerations)
- [TypeScript](#typescript)
- [Common Mistakes](#common-mistakes)
- [Choosing the Right Composable](#choosing-the-right-composable)
- [Common Questions](#common-questions)
- [Advanced Examples](#advanced-examples)
- [Common Use Cases](#common-use-cases)

[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/typescript/head/guides/get-started/overview)
- [useHead](https://unhead.unjs.io/docs/typescript/head/api/composables/use-head)
- [useSeoMeta](https://unhead.unjs.io/docs/typescript/head/api/composables/use-seo-meta)
- [useHeadSafe](https://unhead.unjs.io/docs/typescript/head/api/composables/use-head-safe)
- [useScript](https://unhead.unjs.io/docs/typescript/head/api/composables/use-script)

### Schema.org

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

### Guides

- [Titles](https://unhead.unjs.io/docs/typescript/head/guides/core-concepts/titles)
- [Streaming SSR](https://unhead.unjs.io/docs/typescript/head/guides/core-concepts/streaming)
- [DOM Events](https://unhead.unjs.io/docs/typescript/head/guides/core-concepts/dom-event-handling)
- [Plugins](https://unhead.unjs.io/docs/typescript/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)