With Inertia's <Head> component, how do you set a marketing page's title and meta description without producing duplicate head tags?
answer
- the app renders inside body
- Head title shorthand
- one title, other tags stack
- head-key deduplicates
- data-inertia and x-inertia::head fallback
basics
~20 sRender <Head> from @inertiajs/react or @inertiajs/vue3 with a title prop or child tags; Inertia keeps a single <title>, stacks other tags, and a head-key lets a page replace a layout's meta tag instead of duplicating it.
solid answer
~40 sAn Inertia page renders inside `<body>`, so it cannot write `<head>` markup directly. The `<Head>` component from the React or Vue adapter (Svelte uses its own `<svelte:head>`) collects `<title>`, `<meta>` and `<link>` elements from every instance on the page and writes them into the document head; `<Head title="Pricing" />` is the shorthand. Only one `<title>` is ever rendered, with a page's title overriding its layout's, while other tags stack, so a layout's default description and a page's description would both appear unless both carry the same `head-key`. Managed tags are marked with `data-inertia`; a Blade tag given `data-inertia="description"` is adopted and replaced on navigation. With SSR, the rendered head arrives through `@inertiaHead`, and `<x-inertia::head>` prints its slot, such as a default title, only when SSR did not render, avoiding a second `<title>`.
code
jsx · 17 linesimport { Head } from '@inertiajs/react'
export default function Pricing({ plans }) {
return (
<>
<Head title="Pricing">
<meta
head-key="description"
name="description"
content="Simple plans for teams of every size."
/>
</Head>
<h1>Pricing</h1>
{/* plan cards */}
</>
)
}go deeper
Know that <Head title> sets the page title from an Inertia page and that it can also hold meta tags.
Explain the merge rules: one title, other tags stack, head-key for replacement, and data-inertia on Blade tags the component should adopt.
Make crawlable pages correct under SSR: @inertiaHead output, the x-inertia::head fallback slot, the v3 attribute rename and duplicate-title bugs.
Own the SEO contract for public pages, deciding which tags are layout defaults, which are per page, and how they are tested.
## Why Inertia needs a Head component An **Inertia** page component is mounted into a root element inside `<body>`. The document `<head>` lives in the Blade root view, outside the component tree, so a page cannot simply render a `<title>`. For marketing pages that must be crawlable, the title and description are exactly what search results and link previews show, so they must be right on every page. The React and Vue adapters ship a **`<Head>`** component for this. The Svelte adapter does not, because Svelte has `<svelte:head>` built in. ## Basic use - **Title shorthand**: `<Head title="Pricing" />`. - **Child tags**: `<Head><title>Pricing</title><meta name="description" content="Plans for every team" /></Head>`. - A global `title` callback in the app setup can decorate every title, for example appending the product name. Many `<Head>` instances can exist at once, typically one in a layout and one in the page. Inertia merges them. ## How duplicates are avoided | Tag | Rule | |---|---| | `<title>` | only one is rendered; a later instance overrides an earlier one, so a page overrides its layout | | `<meta>`, `<link>` and others | all instances **stack**, since multiples are valid HTML | | a tag with `head-key="description"` | rendered once per key; a later instance replaces an earlier one | So a layout that sets a default description and a page that sets its own should both use `head-key="description"`; without it, the head carries two description tags and crawlers pick one arbitrarily. ## Tags from the Blade root view The client-side `<Head>` marks the tags it manages with a **`data-inertia`** attribute whose value is the tag's `head-key`. It leaves every other tag in the Blade template alone, except `<title>`, which it always replaces because only one may exist. To let `<Head>` take over a default tag defined in Blade, give it the same key: `<meta data-inertia="description" name="description" content="...">`. On the first client-side navigation, `<Head>` matches it by key and replaces or removes it. In Inertia 3 this attribute is `data-inertia`. Inertia 2 used a bare `inertia` attribute, and templates upgraded from v2 must rename it. ## SSR and the head With server-side rendering, the SSR server renders the page's `<Head>` instances too and returns the tags alongside the body: 1. `@inertiaHead` (or `<x-inertia::head>`) in the Blade `<head>` prints those tags; 2. crawlers that never run JavaScript still see the right title and description; 3. on hydration, the client `<Head>` recognises the tags by their `data-inertia` markers. A classic problem was a fallback `<title>` in Blade plus an SSR-rendered title, giving two titles. Inertia 3's **`<x-inertia::head>`** component solves it: its slot content prints **only when SSR did not render**. Put the app-name default title inside the slot and it appears for client-rendered pages but not on server-rendered ones. ## Without SSR If SSR is off, the initial HTML contains only what the Blade template prints; `<Head>` updates the document after the JavaScript runs. Crawlers that execute JavaScript can see the result; ones that do not see the Blade defaults. That is why crawlable marketing pages pair `<Head>` with SSR. ## Common mistakes - a page description without `head-key` stacking next to the layout's; - a hard-coded `<title>` in Blade next to `@inertiaHead`, doubling the title under SSR; - templates carried over from Inertia 2 still using the bare `inertia` attribute; - importing `Head` in a Svelte app instead of using `<svelte:head>`.
- Why does a page's meta description appear twice in the rendered head?Non-title tags stack across `<Head>` instances. If the layout and the page both emit a description without a shared `head-key`, both are rendered. Giving both `head-key="description"` makes the page's tag replace the layout's.
- Why does a Svelte Inertia page not import Head?The Svelte adapter has no `<Head>` component because Svelte ships `<svelte:head>`, which already renders into the document head, including during SSR.
saying these in an interview costs you the question
- Every <Head> instance adds its own <title> tag
- head-key is needed to stop two titles from rendering
- The Svelte adapter's <Head> works like React's
- The inertia attribute on Blade head tags is still current in v3
- Without SSR, crawlers that skip JavaScript still see the page's Head tags