Quick Answer: In React, pass state values directly to useHead() for reactivity. The component re-renders when state changes, automatically updating head tags.
Unhead integrates with React's state management system through the React Context API. This guide explains how to effectively manage reactive head tags in your React applications.
Unhead uses React's Context API to provide head management throughout your component tree. The integration automatically tracks changes to your React state and updates the document head accordingly.
Unlike React Helmet, Unhead uses the provider pattern for isolation - keeping head state contained and manageable rather than global:
import { createHead, UnheadProvider } from '@unhead/react'
// Create head instance with custom options
const head = createHead()
function App() {
return (
<UnheadProvider head={head}>
<YourApp />
</UnheadProvider>
)
}
This pattern prevents many hydration and testing headaches by keeping head state contained.
React's state management system works naturally with Unhead. When you pass state to useHead(), the head tags update whenever the state changes:
import { useHead } from '@unhead/react'
import { useState } from 'react'
function PageHead() {
const [title, setTitle] = useState('Welcome to My App')
// Head tags will update when title state changes
useHead({
title
})
return (
<button onClick={() => setTitle('Updated Title')}>
Update Title
</button>
)
}
It's common to update head tags based on asynchronous data:
import { useHead } from '@unhead/react'
import { useEffect, useState } from 'react'
function PageHead() {
const [title, setTitle] = useState('Loading...')
useEffect(() => {
async function loadData() {
const response = await fetch('/api/page')
const data = await response.json()
setTitle(data.title)
}
loadData()
}, [])
useHead({
title
})
return null
}
For pages with multiple meta tags, you can manage them together in a structured way:
function ProductHead({ id }) {
const [product, setProduct] = useState({
title: 'Loading...',
description: '',
image: '/placeholder.jpg',
price: ''
})
useEffect(() => {
fetchProduct(id).then(setProduct)
}, [id])
useHead({
title: product.title,
meta: [
{ name: 'description', content: product.description },
{ property: 'og:image', content: product.image },
{ property: 'product:price', content: product.price }
]
})
return null
}
Unhead works well with popular data fetching libraries:
import { useQuery } from '@tanstack/react-query'
import { useHead } from '@unhead/react'
function PageHead({ id }) {
const { data = { title: 'Loading...', description: '' } } = useQuery({
queryKey: ['page', id],
queryFn: () => fetchPage(id)
})
useHead({
title: data.title,
meta: [
{ name: 'description', content: data.description }
]
})
return null
}
import { useHead } from '@unhead/react'
import useSWR from 'swr'
function PageHead({ slug }) {
const { data = { title: 'Loading...', description: '' } } = useSWR(
`/api/pages/${slug}`,
fetcher
)
useHead({
title: data.title,
meta: [
{ name: 'description', content: data.description }
]
})
return null
}
For optimal performance with Unhead in React:
Use useMemo for complex head configurations to prevent unnecessary re-renders:
import { useHead } from '@unhead/react'
import { useMemo } from 'react'
function SEOHead({ title, description, image }) {
const headConfig = useMemo(() => ({
title,
meta: [
{ name: 'description', content: description },
{ property: 'og:title', content: title },
{ property: 'og:description', content: description },
{ property: 'og:image', content: image }
]
}), [title, description, image])
useHead(headConfig)
return null
}
Leverage code-splitting for head components to reduce initial bundle size:
import { lazy } from 'react'
// Load head components only when needed
const ProductHead = lazy(() => import('./ProductHead'))
const BlogHead = lazy(() => import('./BlogHead'))
function App() {
return (
<Routes>
<Route
path="/products/:id"
element={(
<Suspense fallback={null}>
<ProductHead />
</Suspense>
)}
/>
<Route
path="/blog/:slug"
element={(
<Suspense fallback={null}>
<BlogHead />
</Suspense>
)}
/>
</Routes>
)
}
Under the hood, Unhead in React:
useEffect and state hooksThe implementation wraps head entries with React state management that:
function ProductPage({ id }) {
return (
<>
<GlobalHead />
{' '}
{/* Global site metadata */}
<ProductHead id={id} />
{' '}
{/* Product-specific metadata */}
<ProductSchema id={id} />
{' '}
{/* Structured data */}
{/* UI components */}
</>
)
}