Index of every page on this site: llms.txt
React
Get Started

Installing Unhead with React

Install @unhead/react, wrap the application with UnheadProvider, and call useHead() in components. For SSR, inject the rendered tags with transformHtmlTemplate().

Use the <Head> component for JSX-style declarations or useHead() for object input. If you are migrating from react-helmet, Unhead also ships a compatibility component.

The examples follow Vite's SSR structure; the same client entry point works in a React SPA.

Demos

Install Unhead

1. Add Dependency

Install the @unhead/react dependency in your project.

bash
pnpm add @unhead/react

2. Set Up Client-Side Rendering

Create the browser instance from @unhead/react/client. In a Vite SSR app this usually belongs in entry-client.ts; in an SPA, put it in the main entry.

The value prop supplies the head instance for browser, server, and streaming providers. The browser provider also accepts the deprecated head prop for compatibility.

src/entry-client.ts
import { createHead, UnheadProvider } from '@unhead/react/client'
import { StrictMode } from 'react'
import { hydrateRoot } from 'react-dom/client'
import App from './App'
import './index.css'

const head = createHead()

hydrateRoot(
  document.getElementById('root') as HTMLElement,
  <StrictMode>
    <UnheadProvider value={head}>
      <App />
    </UnheadProvider>
  </StrictMode>,
)

3. Set Up Server-Side Rendering

Serving your app as an SPA? You can skip this step. See the SPA guide for template defaults, crawler limits, and route prerendering.

For SSR, create a fresh instance from @unhead/react/server for each request and return it with the rendered app.

src/entry-server.ts
import { createHead, UnheadProvider } from '@unhead/react/server'
import { StrictMode } from 'react'
import { renderToString } from 'react-dom/server'
import App from './App'

export function render(_url: string) {
  const head = createHead()
  const html = renderToString(
    <StrictMode>
      <UnheadProvider value={head}>
        <App />
      </UnheadProvider>
    </StrictMode>,
  )
  return { html, head }
}

After React renders, pass the instance and complete HTML template to transformHtmlTemplate():

server.ts
import { transformHtmlTemplate } from '@unhead/react/server'
// ...

// Serve HTML
app.use('*all', async (req, res) => {
  try {
    // ...

    const rendered = await render(url)

    const html = transformHtmlTemplate(
      rendered.head,
      template.replace(`<!--app-html-->`, rendered.html ?? '')
    )

    res.status(200).set({ 'Content-Type': 'text/html' }).send(html)
  }
  catch (e) {
    // ...
  }
})
// ..

4. Add Head Tags

The server head inserts three defaults:

  • <meta charset="utf-8">
  • <meta name="viewport" content="width=device-width, initial-scale=1">
  • <html lang="en">

Set server-only defaults through init. Keeping them in the server entry also keeps them out of the client bundle.

src/entry-server.ts
import { createHead } from '@unhead/react/server'

export function render(_url: string) {
  const head = createHead({
    // change default initial lang
    init: [
      {
        htmlAttrs: { lang: 'en' },
        title: 'Default title',
        titleTemplate: '%s - My Site',
      },
    ]
  })
  const html = `<!-- your html -->`
  return { html, head }
}

To add tags in your components, use either the <Head> component or the useHead() hook.

  • useHead() accepts the full typed head object and entry options.
  • <Head> accepts native head elements as children.
App.tsx
import { Head, useHead } from '@unhead/react'

export default function App() {
  // a. use the hook
  useHead({
    title: 'Example Site',
    meta: [
      { name: 'description', content: 'An example page description.' }
    ]
  })
  // b. use the component
  return (
    <div>
      <Head>
        <title>Example Site</title>
        <meta name="description" content="An example page description." />
      </Head>
      <h1>Hello World</h1>
    </div>
  )
}

5. Enable Auto-Imports (Optional)

unplugin-auto-import can provide the composables without explicit imports:

vite.config.ts
import { hookImports } from '@unhead/react'
import AutoImport from 'unplugin-auto-import/vite'

export default defineConfig({
  plugins: [
    AutoImport({
      imports: [
        hookImports,
      ],
    }),
    // ...
  ]
})

Next Steps

Hooks and components:

For react-helmet, see the migration guide.

Optional integrations:

Did this page help you?