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:
- Uses Solid.js's Context API for head instance management
- Integrates with Solid.js's reactivity system using
createEffect
- Creates head entries that update reactively when input props change
- 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
- Leverage Solid.js's Reactivity: Use signals, memos, and resources for reactive head data.
- Handle Loading States: Always provide fallback values for async data to prevent undefined errors.
- 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 */}
</>
)
}
- Keep Head Components Focused: Create dedicated components for head management that are separate from UI components.
- Cleanup Happens Automatically: Unhead handles cleanup when components are disposed through Solid.js's cleanup system.