Reactivity

Learn how to handle state changes for head tags in React - from basic state updates to complex data flows.

Introduction

Unhead works in React by attaching to the React context. This allows you to manage head tags across your app with ease.

However, working within the React context can be tricky. This guide will help you understand how to manage head tags across async operations and server/client boundaries.

Using the UnheadProvider

Unlike React Helmet, Unhead uses the provider pattern for isolation - keeping head state contained and manageable rather than global.

This prevents many of the hydration and testing headaches.

import { createHead, UnheadProvider } from '@unhead/react/client'

// Create head instance with custom options
const head = createHead()

function App() {
  return (
    <UnheadProvider head={head}>
      <YourApp />
    </UnheadProvider>
  )
}

Async Head Updates

It's common to update head tags based on async data, in React we ne

Basic State Updates

The simplest way to handle async data with head tags:

import { useHead } from '@unhead/react'
import { useEffect, useState } from 'react'

function PageHead() {
  const [title, setTitle] = useState('Loading...')

  useEffect(() => {
    async function loadData() {
      const data = await fetch('/api/page')
      setTitle(data.title)
    }
    loadData()
  }, [])

  useHead({
    title
  })

  return null
}

Complex Head Data

For pages with lots of meta tags, manage them together:

function ProductHead({ id }) {
  const [product, setProduct] = useState({
    title: 'Loading...',
    description: '',
    image: '/placeholder.jpg',
    price: ''
  })

  useHead({
    title: product.title,
    meta: [
      { name: 'description', content: product.description },
      { property: 'og:image', content: product.image },
      { property: 'product:price', content: product.price }
    ]
  })

  useEffect(() => {
    fetchProduct(id).then(setProduct)
  }, [id])

  return null
}

Using with Data Libraries

React Query Example

import { useQuery } from '@tanstack/react-query'
import { useHead } from '@unhead/react'

function PageHead({ id }) {
  const { data } = useQuery({
    queryKey: ['page', id],
    queryFn: () => fetchPage(id),
    placeholderData: {
      title: 'Loading...',
      description: ''
    }
  })

  useHead({
    title: data.title,
    meta: [
      { name: 'description', content: data.description }
    ]
  })

  return null
}

Performance Tips

  1. Use useCallback for handlers:
const updateHead = useCallback((data) => {
  head.push({
    title: data.title
  })
}, [])
  1. Memoize complex configurations:
const headConfig = useMemo(() => ({
  title,
  meta: [
    { name: 'description', content },
    { property: 'og:title', content: title },
    // More meta tags
  ]
}), [title, content])

useHead(headConfig)
  1. Split head components for code-splitting benefits
const ProductHead = lazy(() => import('./ProductHead'))
Did this page help you?