The Head component lets you render elements into the document <head> on web, and integrates with native platform features like Apple Handoff and Spotlight Search on iOS.
app/blog/[slug].tsx
import { Head, useLoader } from 'one'
export async function loader({ params }) {
const post = await getPost(params.slug)
return { post }
}
export default function BlogPost() {
const { post } = useLoader()
return (
<>
<Head>
<title>{post.title}</title>
<meta name="description" content={post.excerpt} />
<meta property="og:title" content={post.title} />
<meta property="og:description" content={post.excerpt} />
<meta property="og:image" content={post.image} />
</Head>
<article>
<h1>{post.title}</h1>
{/* ... */}
</article>
</>
)
}
| Prop | Type | Description |
|---|---|---|
children | ReactNode | Standard HTML head elements like <title>, <meta>, <link>, etc. |
The Head component accepts standard HTML head elements:
<title> - Page title<meta> - Meta tags (description, Open Graph, Twitter Cards, etc.)<link> - Stylesheets, favicons, preload hints<script> - External scripts<style> - Inline styles<Head>
{/* Page title */}
<title>My Page Title</title>
{/* Meta tags */}
<meta name="description" content="Page description" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
{/* Open Graph */}
<meta property="og:title" content="My Page" />
<meta property="og:description" content="Page description" />
<meta property="og:image" content="https://example.com/image.png" />
<meta property="og:url" content="https://example.com/page" />
{/* Twitter Cards */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="My Page" />
{/* Favicon */}
<link rel="icon" href="/favicon.ico" />
{/* Preload fonts */}
<link rel="preload" href="/fonts/custom.woff2" as="font" type="font/woff2" crossOrigin="" />
</Head>
Use loader data or component state to set dynamic metadata:
app/products/[id].tsx
import { Head, useLoader, useParams } from 'one'
export async function loader({ params }) {
const product = await getProduct(params.id)
return { product }
}
export default function ProductPage() {
const { product } = useLoader()
const ogImageUrl = `/api/og?title=${encodeURIComponent(product.name)}&price=${product.price}`
return (
<>
<Head>
<title>{product.name} | My Store</title>
<meta name="description" content={product.description} />
<meta property="og:title" content={product.name} />
<meta property="og:image" content={ogImageUrl} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="product:price:amount" content={String(product.price)} />
<meta property="product:price:currency" content="USD" />
</Head>
{/* Page content */}
</>
)
}
On web, Head renders its children directly into the document’s <head> element. Multiple Head components can be used across your app - they will all contribute to the final head content.
On iOS, Head integrates with native platform features:
Enable these features using special meta properties:
<Head>
<title>My Article</title>
<meta property="og:title" content="My Article" />
<meta property="og:description" content="Article description" />
<meta property="og:url" content="https://example.com/article" />
{/* Enable Apple Handoff */}
<meta property="expo:handoff" content="true" />
{/* Enable Spotlight Search indexing */}
<meta property="expo:spotlight" content="true" />
</Head>
| Meta Property | Description |
|---|---|
expo:handoff | Set to "true" to enable Apple Handoff for this page |
expo:spotlight | Set to "true" to index this page in iOS Spotlight Search |
On Android, Head currently has no effect. Native metadata features may be added in future versions.
For consistent metadata across your app, create a wrapper component:
features/HeadInfo.tsx
import { Head } from 'one'
type HeadInfoProps = {
title: string
description?: string
image?: string
url?: string
}
export function HeadInfo({ title, description, image, url }: HeadInfoProps) {
const fullTitle = `${title} | My App`
return (
<Head>
<title>{fullTitle}</title>
{description && <meta name="description" content={description} />}
{/* Open Graph */}
<meta property="og:title" content={fullTitle} />
{description && <meta property="og:description" content={description} />}
{image && <meta property="og:image" content={image} />}
{url && <meta property="og:url" content={url} />}
{/* Twitter */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={fullTitle} />
{description && <meta name="twitter:description" content={description} />}
{image && <meta name="twitter:image" content={image} />}
</Head>
)
}
Use it in your pages:
import { HeadInfo } from '~/features/HeadInfo'
export default function AboutPage() {
return (
<>
<HeadInfo
title="About Us"
description="Learn about our company"
image="/images/about-og.png"
/>
{/* Page content */}
</>
)
}
Edit this page on GitHub.