useHtml()
Control the document head from any component — titles, meta tags, Open Graph, canonical links, JSON-LD, and HTML attributes.
Basic usage
Call useHtml() from any server or client component. Calls in child components override calls in parent layouts for the same keys.
app/pages/about.tsxtypescript
import { useHtml } from 'nukejs'
export default function About() {
useHtml({
title: 'About Us',
meta: [
{ name: 'description', content: 'Learn about our team and mission.' },
{ property: 'og:title', content: 'About Us' },
{ property: 'og:description', content: 'Learn about our team.' },
{ property: 'og:image', content: 'https://mysite.com/og.png' },
{ name: 'twitter:card', content: 'summary_large_image' },
],
link: [
{ rel: 'canonical', href: 'https://mysite.com/about' },
],
htmlAttrs: { lang: 'en' },
bodyAttrs: { className: 'page-about' },
})
return <main>...</main>
}All options
| Option | Type | Description |
|---|---|---|
| title | string | (prev: string) => string | Page title. Pass a function to compose with layout titles. |
| meta | object[] | Each object becomes a <meta> element. |
| link | object[] | Each object becomes a <link> element. |
| script | object[] | Script tags. Supports type, src, inline children, and position ("head" | "body"). |
| htmlAttrs | object | Attributes set on <html> (e.g. lang). |
| bodyAttrs | object | Attributes set on <body>. |
Title composition across layouts
app/pages/layout.tsxtypescript
// Root layout: wraps every page title
useHtml({ title: (prev) => `${prev} | My Site` })app/pages/about.tsxtypescript
// Page sets the base title
useHtml({ title: 'About Us' })
// Result: "About Us | My Site"The page title is always the base. Layout functions wrap it outward, from innermost to outermost layout.
Inline scripts
Pass children in a script entry for inline JavaScript. Use src for external scripts:
app/pages/layout.tsxtypescript
useHtml({
script: [
// External async script
{ src: 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXX', type: 'text/javascript' },
// Inline init script
{ type: 'text/javascript', children: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXX');
` },
]
})Script position: head vs body
Each script entry accepts a position field that controls where it is injected in the HTML document.
| position | Where it lands | When to use |
|---|---|---|
"head" (default) | Inside <head> | Theme detection, critical inline scripts that must run before first paint. |
"body" | End of <body> | Analytics, tracking pixels, and third-party loaders that should not block rendering. |
app/pages/layout.tsxtypescript
useHtml({
script: [
// Analytics loader — injected at end of <body>, won't block rendering
{
src: 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX',
async: true,
position: 'body',
},
// Inline init — also deferred to body, must follow the loader above
{
children: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
`,
position: 'body',
},
// Theme detection — must run before first paint, stays in <head>
{
children: `
const t = localStorage.getItem('theme') ?? 'light';
document.documentElement.classList.add(t);
`,
// position defaults to 'head'
},
]
})Inline JSON-LD structured data
app/pages/index.tsxtypescript
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
name: 'NukeJS',
url: 'https://nukejs.com',
applicationCategory: 'DeveloperApplication',
offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
}
useHtml({
title: 'NukeJS',
script: [
{ type: 'application/ld+json', children: JSON.stringify(jsonLd) }
]
})Setting defaults in the root layout
Set base values once in app/pages/layout.tsx. Individual pages only override what differs:
app/pages/layout.tsxtypescript
import { useHtml } from 'nukejs'
const SITE = 'https://mysite.com'
export default function RootLayout({ children }: { children: React.ReactNode }) {
useHtml({
title: (t) => `${t} | Acme Corp`,
meta: [
{ name: 'description', content: 'The default site description.' },
{ property: 'og:site_name', content: 'Acme Corp' },
{ property: 'og:image', content: `${SITE}/og-default.png` },
{ name: 'twitter:card', content: 'summary_large_image' },
],
htmlAttrs: { lang: 'en' },
})
return <>{children}</>
}