Managing Head State in React
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
- Use
useCallback
for handlers:
const updateHead = useCallback((data) => {
head.push({
title: data.title
})
}, [])
- Memoize complex configurations:
const headConfig = useMemo(() => ({
title,
meta: [
{ name: 'description', content },
{ property: 'og:title', content: title },
// More meta tags
]
}), [title, content])
useHead(headConfig)
- Split head components for code-splitting benefits
const ProductHead = lazy(() => import('./ProductHead'))
Did this page help you?