---
title: "Reactivity in React"
description: "Use useState and useEffect with useHead() for reactive head tags. Integrates with React Query, SWR, and async data fetching."
canonical_url: "https://unhead.unjs.io/docs/react/head/guides/core-concepts/reactivity"
last_updated: "2026-07-21T18:40:57.637Z"
---

Pass React state directly to `useHead()`. A re-render patches the component's existing head entry.

## React Integration

`UnheadProvider` places one head instance in React context. `useHead()` reads that instance, creates an entry, and patches it after renders.

### Provide the Head Instance

Create the client instance once, then pass it to `UnheadProvider`:

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

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

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

## Reactive Head Tags

Pass state values in the head object:

```tsx
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>
  )
}
```

## Async Data

Update state when the request completes; the next render updates the entry:

```tsx
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
}
```

## Group Related Tags

Keep related tags in the same entry when they come from the same data:

```tsx
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
}
```

## Data-Fetching Libraries

React Query and SWR require no Unhead-specific integration. Pass their result to `useHead()` as you would local state.

### React Query

```tsx
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
}
```

### SWR

```tsx
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
}
```

## Memoizing Large Inputs

### Memoize Complex Head Configurations

If building the head object is expensive, memoize it with the same dependencies as the page data:

```tsx
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
}
```

## Entry Lifecycle

On the server, `useHead()` creates its entry during render. In the browser, it creates the entry in an effect, patches it when the input changes, and disposes it when the component unmounts.

React runs an effect's cleanup before rerunning the setup after a dependency changes and when the component unmounts. In development, Strict Mode also runs one extra setup-and-cleanup cycle; Unhead's disposal follows that [documented effect lifecycle](https://react.dev/reference/react/useEffect#caveats), so the rehearsal does not leave a duplicate entry behind.
