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.
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"). |
| style | object[] | Inline <style> blocks injected into <head>. Each entry accepts content (raw CSS) and an optional media attribute. |
| htmlAttrs | object | Attributes set on <html> (e.g. lang). |
| bodyAttrs | object | Attributes set on <body>. |
Inline style injection
The style option injects <style> blocks into <head>. This is useful for critical CSS that must be present before first paint, or for per-page styles that vary by route:
import { useHtml } from 'nukejs'
export default function Dashboard() {
useHtml({
style: [
{ content: `.chart { height: 400px; }` },
{ content: `.sidebar { width: 260px; }`, media: '(min-width: 768px)' },
],
})
return <main>...</main>
}| Field | Type | Description |
|---|---|---|
content | string | Raw CSS injected inside a <style> tag. |
media | string (optional) | Value for the media attribute — e.g. 'print', '(prefers-color-scheme: dark)'. |
Title composition across layouts
// Root layout: wraps every page title
useHtml({ title: (prev) => `${prev} | My Site` })// 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:
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. |
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
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:
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}</>
}