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

OptionTypeDescription
titlestring | (prev: string) => stringPage title. Pass a function to compose with layout titles.
metaobject[]Each object becomes a <meta> element.
linkobject[]Each object becomes a <link> element.
scriptobject[]Script tags. Supports type, src, inline children, and position ("head" | "body").
htmlAttrsobjectAttributes set on <html> (e.g. lang).
bodyAttrsobjectAttributes 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.

positionWhere it landsWhen 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}</>
}