---
title: "Reactivity in Solid.js"
description: "Use Solid.js signals with useHead() for reactive head tags. Track signals in createEffect() and update entries with patch()."
canonical_url: "https://unhead.unjs.io/docs/solid-js/head/guides/core-concepts/reactivity"
last_updated: "2026-08-11T00:43:23.477Z"
---

Create one entry with `useHead()`, read signals in `createEffect()`, and patch that entry when they change.

<note>

[Solid effects do not run during SSR](https://docs.solidjs.com/reference/basic-reactivity/create-effect#server-side-rendering). Pass initial values or getters to `useHead()` when tags must appear in the server response, then use an effect to patch later client-side changes.

</note>

## Solid Integration

Unhead for Solid.js uses Solid context for the head instance and Solid's [`onCleanup()` lifecycle](https://docs.solidjs.com/reference/lifecycle/on-cleanup) to dispose entries. To update an entry when a signal changes, track the signal in `createEffect()` and call `patch()`.

### Provide the Head Instance

Provide one head instance at the application root:

```tsx
import { createHead, UnheadContext } from '@unhead/solid-js/client'
import { render } from 'solid-js/web'
import App from './App'

const head = createHead()

render(() => (
  <UnheadContext.Provider value={head}>
    <App />
  </UnheadContext.Provider>
), document.getElementById('root'))
```

## Signals

Create the head entry once, then patch it from an effect that reads the signal:

```tsx
import { useHead } from '@unhead/solid-js'
import { createEffect, createSignal } from 'solid-js'

function PageHead() {
  const [title, setTitle] = createSignal('Welcome to My App')

  const entry = useHead({ title: title() })

  createEffect(() => {
    entry.patch({ title: title() })
  })

  return (
    <button onClick={() => setTitle('Updated Title')}>
      Update Title
    </button>
  )
}
```

### Updating Several Values

Read every dependency inside the effect and patch the complete entry:

```tsx
import { useHead } from '@unhead/solid-js'
import { createEffect, createSignal } from 'solid-js'

function PageHead() {
  const [title, setTitle] = createSignal('Welcome to My App')
  const [description, setDescription] = createSignal('My site description')

  const entry = useHead({
    title: title(),
    meta: [{ name: 'description', content: description() }],
  })

  createEffect(() => {
    entry.patch({
      title: title(),
      meta: [
        { name: 'description', content: description() }
      ]
    })
  })

  return (
    <>
      <button onClick={() => setTitle('Updated Title')}>Update Title</button>
      <button onClick={() => setDescription('New description')}>Update Description</button>
    </>
  )
}
```

## Async Data

Patch the entry when a resource settles:

```tsx
import { useHead } from '@unhead/solid-js'
import { createEffect, createResource } from 'solid-js'

async function fetchPageData(id) {
  const response = await fetch(`/api/page/${id}`)
  return response.json()
}

function PageHead({ id }) {
  const [pageData] = createResource(() => id, fetchPageData)

  const entry = useHead()

  createEffect(() => {
    entry.patch({
      title: pageData()?.title || 'Loading...',
      meta: [
        {
          name: 'description',
          content: pageData()?.description || 'Loading page content...'
        }
      ]
    })
  })

  return null
}
```

## Group Related Tags

Put tags derived from the same resource in one entry:

```tsx
import { useHead } from '@unhead/solid-js'
import { createEffect, createResource } from 'solid-js'

async function fetchProduct(id) {
  const response = await fetch(`/api/products/${id}`)
  return response.json()
}

function ProductHead({ id }) {
  const [product] = createResource(() => id, fetchProduct)

  const entry = useHead()

  createEffect(() => {
    entry.patch({
      title: product()?.title || 'Loading Product...',
      meta: [
        { name: 'description', content: product()?.description || '' },
        { property: 'og:image', content: product()?.image || '/placeholder.jpg' },
        { property: 'product:price', content: product()?.price || '' }
      ]
    })
  })

  return null
}
```

## Reusable Head Components

A small wrapper component can own repeated SEO tags:

```tsx
import { useHead } from '@unhead/solid-js'
import { createEffect, mergeProps } from 'solid-js'

function SEOHead(props) {
  const merged = mergeProps({
    title: 'Default Title',
    description: 'Default description',
    ogImage: '/default-og.jpg'
  }, props)

  const entry = useHead()

  createEffect(() => {
    entry.patch({
      title: merged.title,
      meta: [
        { name: 'description', content: merged.description },
        { property: 'og:title', content: merged.title },
        { property: 'og:description', content: merged.description },
        { property: 'og:image', content: merged.ogImage }
      ]
    })
  })

  return null
}

// Usage
function HomePage() {
  return (
    <div>
      <SEOHead
        title="Home Page"
        description="Welcome to our website"
      />
      {/* Page content */}
    </div>
  )
}
```

## Conditional Tags

Patch the complete entry when a condition changes:

```tsx
import { useHead } from '@unhead/solid-js'
import { createEffect, createSignal, Show } from 'solid-js'

function DynamicHead() {
  const [isLoggedIn, setIsLoggedIn] = createSignal(false)
  const [user, setUser] = createSignal<{ name: string, id: string } | null>(null)
  const entry = useHead()

  // Simulate login
  const login = () => {
    setUser({ name: 'John Doe', id: '123' })
    setIsLoggedIn(true)
  }

  createEffect(() => {
    if (isLoggedIn()) {
      entry.patch({
        title: `Dashboard - ${user()!.name}`,
        meta: [
          { name: 'description', content: 'Your personal dashboard' }
        ]
      })
    }
    else {
      entry.patch({
        title: 'Login',
        meta: [
          { name: 'description', content: 'Login to access your dashboard' },
          { name: 'robots', content: 'noindex' }
        ]
      })
    }
  })

  return (
    <div>
      <Show when={isLoggedIn()} fallback={<button onClick={login}>Login</button>}>
        <h1>
          Welcome,
          {user()!.name}
          !
        </h1>
      </Show>
    </div>
  )
}
```

## Entry Cleanup

The adapter registers each entry with Solid's current owner. `onCleanup()` disposes it with the component.
