Solid.js
Core Concepts

Reactivity in Solid.js

Introduction

Unhead integrates seamlessly with Solid.js's fine-grained reactivity system. This guide explains how to effectively manage reactive head tags in your Solid.js applications.

How Unhead Works in Solid.js

Unhead for Solid.js leverages Solid's reactive primitives including signals, effects, and contexts to provide a reactive head management solution. When your application state changes, Solid.js's reactivity system automatically ensures your head tags reflect the current state.

Using the UnheadContext

Unhead uses Solid.js's Context API to provide head management throughout your component tree:

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'))

This pattern keeps head state contained and manageable, making components more testable and preventing hydration issues.

Basic Reactivity with Signals

Solid.js's signals work naturally with Unhead. When you pass signals to useHead(), the head tags update whenever the signal values change:

import { useHead } from '@unhead/solid-js'
import { createSignal } from 'solid-js'

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

  // Head tags will update when title signal changes
  useHead(() => ({
    title: title()
  }))

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

Direct Signal Integration

You can also pass signals directly to useHead():

import { useHead } from '@unhead/solid-js'
import { createSignal } from 'solid-js'

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

  useHead({
    get title() { return title() },
    meta: [
      {
        name: 'description',
        get content() { return description() }
      }
    ]
  })

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

Async Head Updates

It's common to update head tags based on asynchronous data:

import { useHead } from '@unhead/solid-js'
import { createResource, createSignal } 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)

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

  return null
}

Managing Complex Head Data

For pages with multiple meta tags, you can manage them together with Solid.js's reactive primitives:

import { useHead } from '@unhead/solid-js'
import { 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)

  useHead(() => ({
    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
}

Creating Reusable Head Components

Solid.js's composable nature makes it easy to create reusable head components:

import { useHead } from '@unhead/solid-js'
import { mergeProps } from 'solid-js'

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

  useHead(() => ({
    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>
  )
}

Dynamic Head Tags with Control Flow

You can leverage Solid.js's control flow primitives to conditionally render head tags:

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

function DynamicHead() {
  const [isLoggedIn, setIsLoggedIn] = createSignal(false)
  const [user, setUser] = createSignal(null)

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

  createEffect(() => {
    if (isLoggedIn()) {
      useHead({
        title: `Dashboard - ${user().name}`,
        meta: [
          { name: 'description', content: 'Your personal dashboard' }
        ]
      })
    }
    else {
      useHead({
        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>
  )
}

Implementation Details

Under the hood, Unhead in Solid.js:

  1. Uses Solid.js's Context API for head instance management
  2. Integrates with Solid.js's reactivity system using createEffect
  3. Creates head entries that update reactively when input props change
  4. Automatically cleans up with onCleanup when components are disposed

The implementation wraps head entries with Solid.js's reactive primitives that:

  • Patch the entry when input data changes
  • Dispose of the entry when the component is disposed

Best Practices

  1. Leverage Solid.js's Reactivity: Use signals, memos, and resources for reactive head data.
  2. Handle Loading States: Always provide fallback values for async data to prevent undefined errors.
  3. Use Component Composition: Compose multiple head components for complex pages:
function ProductPage(props) {
  return (
    <>
      <GlobalHead />
      <ProductHead id={props.id} />
      <ProductSchema id={props.id} />
      {/* UI components */}
    </>
  )
}
  1. Keep Head Components Focused: Create dedicated components for head management that are separate from UI components.
  2. Cleanup Happens Automatically: Unhead handles cleanup when components are disposed through Solid.js's cleanup system.
Did this page help you?