Skip to content

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.

proptypedefaultrequireddescription
linkExtendLinkTag[]-NoArray of <link> tag attribute objects to inject. Each object maps directly to HTML attributes on the tag. Supports an additional prefetch boolean attribute.
metaExtendMetaTag[]-NoArray of <meta> tag attribute objects to inject. Each object must include at least property plus any additional HTML attributes.
customstring | string[]-NoRaw 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" }.

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"
/>

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>

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 config
eminence({
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" />

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" />

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.

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.

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.

---
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} />)}