Index of every page on this site: llms.txt
TypeScript
Core Concepts

Head Tag Inner Content for Scripts, Styles, and JSON

Implementation overview

Configure inline content for script, style, noscript, and template tags with Unhead. This guide compares children, innerHTML, and textContent, including safe JSON serialization and XSS handling.

Use textContent or innerHTML to set inline content on script, style, and noscript entries. Neither property makes untrusted JavaScript, CSS, or HTML safe.

Inline content

import { useHead } from 'unhead'

useHead(unheadInstance, {
  script: [
    { textContent: 'window.analytics = window.analytics || []' }
  ],
  style: [
    { textContent: 'body { background: salmon; color: cyan; }' }
  ]
})

On the client, textContent is assigned through the DOM property while innerHTML is assigned through the corresponding HTML property. During SSR, both values are serialized as raw-element content. Unhead escapes closing script, style, and noscript sequences so the content cannot terminate its element early, but it does not sanitize the JavaScript, CSS, or HTML itself.

Treat both textContent and innerHTML as trusted-code APIs for scripts and styles. Safety depends on the parsing context: textContent is a safe sink for ordinary page text, but text inside <script> or <style> is still executable code. OWASP classifies direct script and style content as dangerous contexts, and MDN documents innerHTML as an HTML injection sink. HTML sanitizers do not make arbitrary JavaScript or CSS safe to execute.

For a title, Unhead HTML-escapes the text during SSR. For JSON-like script types, it serializes object values and escapes < as \u003C.

String shorthand

A string entry is shorthand for innerHTML on script, style, and noscript tags:

import { useHead } from 'unhead'

useHead(unheadInstance, {
  script: ['window.analytics = window.analytics || []'],
  style: ['body { background: salmon; color: cyan; }']
})

Use the object form when you need attributes, a custom deduplication key, or an explicit content property.

JSON data

JSON-like script types accept an object in textContent or innerHTML. Unhead sets type="application/json" when an object is provided without a type.

import { useHead } from 'unhead'

useHead(unheadInstance, {
  script: [
    {
      id: 'site-config',
      type: 'application/json',
      textContent: {
        apiEndpoint: '/api/v1',
        features: { darkMode: true, comments: false }
      }
    }
  ]
})

JSON-LD, import maps, and speculation rules receive the same JSON escaping during tag resolution.

JSON accepted by useHeadSafe

useHeadSafe() accepts JSON script content only when the script type ends in json. It parses and serializes the JSON again, removes prototype-pollution keys, and rejects invalid JSON. It removes executable scripts, inline style content, noscript content, and every innerHTML value.

External files

Use an external script or stylesheet when the content is large, cacheable, or maintained separately:

import { useHead } from 'unhead'

useHead(unheadInstance, {
  link: [{ rel: 'stylesheet', href: '/assets/app.css' }],
  script: [{ src: '/assets/analytics.js', defer: true }]
})

For loading triggers, API readiness, and callbacks, use useScript().

See Also

Did this page help you?