Solid.js
Composables

useHead()

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/solid-js'

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

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
    • Sorted (based on tag priority) - see Tag 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

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

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

Parameters

ParameterTypeRequiredDescription
inputHeadYesThe head configuration object
optionsHeadEntryOptionsNoConfiguration options for the head entry

Returns

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

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/solid-js'

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

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'

  // 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 and Template Parameters in their dedicated guides.
import { useHead } from '@unhead/solid-js'

useHead({
  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.

Reactivity

Manual Control

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

import { useHead } from '@unhead/solid-js'

// 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

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:

Advanced Examples

Title Template

import { useHead } from '@unhead/solid-js'

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

For more details on title templates, see the Titles guide.

Combining Multiple Head Entries

import { useHead } from '@unhead/solid-js'

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

// Page-specific entries (will be merged with globals)
useHead({
  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

import { useHead } from '@unhead/solid-js'
import { computed, ref } from 'vue'

// Initial setup
const data = ref(null)
const loading = ref(true)
const error = ref(null)

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

// Fetch data and update head
async function fetchProduct(id) {
  try {
    loading.value = true
    data.value = await api.getProduct(id)
  }
  catch (err) {
    error.value = err
  }
  finally {
    loading.value = false
  }
}
This pattern works well with data fetching libraries and state management solutions.

Priority-Based Tag Ordering

import { useHead } from '@unhead/solid-js'

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

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

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

Common Use Cases

Here are some common use cases for useHead():

  • Setting page-specific metadata for SEO (consider using useSeoMeta() for a more convenient API)
  • Managing document title and favicon (see Titles guide)
  • Adding external scripts and stylesheets (consider using useScript() for scripts)
  • Setting Open Graph and Twitter card tags

For ready-to-use implementations of common patterns, see our Starter Recipes.

Did this page help you?