Extend
Extend injects additional <link> tags, <meta> tags, and raw HTML fragments into the document head. It is designed for one-off or advanced head customizations that are not covered by the other components in this suite.
| prop | type | default | required | description |
|---|---|---|---|---|
link | ExtendLinkTag[] | - | No | Array of <link> tag attribute objects to inject. Each object maps directly to HTML attributes on the tag. Supports an additional prefetch boolean attribute. |
meta | ExtendMetaTag[] | - | No | Array of <meta> tag attribute objects to inject. Each object must include at least property plus any additional HTML attributes. |
custom | string | string[] | - | No | Raw HTML fragment or array of fragments injected verbatim with no escaping. Only use trusted or pre-sanitized content. |
When no props are provided at all, the entire headTags.extend integration config object is used as the source for all three. See the Automatic example and Decisions Made.
ExtendLinkTag and ExtendMetaTag are just objects representing the attributes for <link> and <meta> tags, respectively. Just pass objects with the desired attributes, and Extend handles the rendering. For example, { rel: "preconnect", href: "https://cdn.example.com" }.
Examples
Section titled “Examples”Inject a <link> tag and a <meta> tag using the link and meta props.
Input:
<Extend link={[{ rel: "preconnect", href: "https://cdn.example.com" }]} meta={[{ property: "custom:token", content: "abc123" }]}/>Output:
<link rel="preconnect" href="https://cdn.example.com" /><meta property="custom:token" content="abc123"/>Custom fragments
Section titled “Custom fragments”Inject raw HTML using the custom prop. Accepts a single string or an array of strings, each injected verbatim.
Input:
<Extend custom={[ '<meta name="custom-a" content="1">', '<script type="application/json">{"key":"value"}</script>', ]}/>Output:
<meta name="custom-a" content="1" /><script type="application/json"> { "key": "value" }</script>Automatic
Section titled “Automatic”When no props are provided at all, the entire headTags.extend integration config is used as the source for all three outputs.
Input:
// in astro.config.mjs integration configeminence({ headTags: { extend: { link: [{ rel: "dns-prefetch", href: "https://assets.example.com" }], meta: [{ property: "custom:source", content: "integration" }], custom: '<meta name="custom-inline" content="from-config">', }, },});<Extend />Output:
<link rel="dns-prefetch" href="https://assets.example.com" /><meta property="custom:source" content="integration"/><meta name="custom-inline" content="from-config" />Complete
Section titled “Complete”All three props provided simultaneously.
Input:
<Extend link={[{ rel: "preconnect", href: "https://cdn.example.com" }]} meta={[{ property: "custom:token", content: "abc123" }]} custom='<meta name="custom-inline" content="value">'/>Output:
<link rel="preconnect" href="https://cdn.example.com" /><meta property="custom:token" content="abc123"/><meta name="custom-inline" content="value" />Decisions Made
Section titled “Decisions Made”Fallback is all-or-nothing
Section titled “Fallback is all-or-nothing”If any prop is provided, the integration config is ignored entirely — all three resolve from props only, with unset props defaulting to empty. Only when no props are passed at all does config.extend supply the values for all three. This avoids partial merges where some tags come from props and others silently come from config.
Raw HTML is not escaped
Section titled “Raw HTML is not escaped”The custom prop is injected with Astro’s set:html directive, bypassing HTML escaping. This is intentional to support arbitrary head markup such as inline scripts and structured data. Only pass content from trusted sources.
No output when all sources are absent
Section titled “No output when all sources are absent”When a prop is omitted and no corresponding integration config value exists, that slot renders nothing. Extend emits no output at all when all three sources are absent.
Source code
Section titled “Source code”---import { hasAnyProp } from "../utils";import type { HTMLAttributes } from "astro/types";import config from "virtual:eminence-astro-suite/head-tags";
type ExtendLinkTag = HTMLAttributes<"link"> & { prefetch?: boolean };type ExtendMetaTag = HTMLAttributes<"meta"> & { property: string };
interface Props { /** * Free-form `<link>` tags to inject. * Supports arbitrary attributes, including `prefetch`. */ link?: ExtendLinkTag[]; /** * Free-form `<meta>` tags to inject. * Each item should include at least `property` and any additional attributes. */ meta?: ExtendMetaTag[]; /** * Raw HTML fragment(s) injected with Astro `set:html`. * The value is not escaped. Only use trusted or pre-sanitized content. */ custom?: string | string[];}
const { link = [], meta = [], custom = [],}: Props = hasAnyProp(Astro.props) ? Astro.props : (config.extend ?? {});
const customHtmlFragments = typeof custom === "string" ? [custom] : Array.isArray(custom) ? custom : [];---
{link.map((attributes: ExtendLinkTag) => <link {...attributes} />)}{meta.map((attributes: ExtendMetaTag) => <meta {...attributes} />)}{customHtmlFragments.map((fragment) => <Fragment set:html={fragment} />)}