Skip to content

Icons

Icons renders icon-related <link> tags from the build-time configuration resolved by the integration. The component is intentionally thin: file names, MIME types, and inferred sizes are computed before the virtual module is serialized, and the component only renders the resolved tags.

When you pass the icons prop, it merges over the integration-provided tags for that render. Matching entries are deduplicated by href, with later runtime entries overriding earlier build-time entries.

proptypedefaultrequireddescription
iconsRecord<string, IconTag | false>{}NoPer-href overrides. Each key is the tag’s href. An IconTag value replaces a matching build-time entry or adds a new one. false removes a matching build-time tag from the rendered output.

Input:

<Icons />

Output:

<link rel="icon" href="/favicon.ico" type="image/x-icon" />
<link
rel="apple-touch-icon"
href="/apple-touch-icon.png"
sizes="180x180"
type="image/png"
/>

When the integration is configured with an SVG source and no explicit favicon.svg entry, all five default icons are generated and a copied SVG favicon tag is automatically prepended. These are the defaults produced when only source is set:

  • favicon.ico — multi-size ICO (16, 32, 48)
  • favicon.png — 32×32 raster favicon
  • apple-touch-icon.png — 180×180 Apple touch icon
  • icon-192.png — 192×192 manifest icon
  • icon.png — 512×512 manifest icon

Input:

<Icons />

Output:

<link rel="icon" href="/favicon.svg" sizes="any" type="image/svg+xml" />
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
<link rel="icon" href="/favicon.png" sizes="32x32" type="image/png" />
<link
rel="apple-touch-icon"
href="/apple-touch-icon.png"
sizes="180x180"
type="image/png"
/>
<link rel="icon" href="/icon-192.png" sizes="192x192" type="image/png" />
<link rel="icon" href="/icon.png" sizes="512x512" type="image/png" />

Input:

<Icons
icons={{
"/favicon.ico": false,
"/campaign.png": {
rel: "icon",
href: "/campaign.png",
sizes: "32x32",
type: "image/png",
media: "dark",
},
}}
/>

Output:

Assuming the integration resolved favicon.ico, favicon.svg, and apple-touch-icon.png into config.icons:

<link rel="icon" href="/favicon.svg" sizes="any" type="image/svg+xml" />
<link
rel="apple-touch-icon"
href="/apple-touch-icon.png"
sizes="180x180"
type="image/png"
/>
<link
rel="icon"
href="/campaign.png"
sizes="32x32"
type="image/png"
media="(prefers-color-scheme: dark)"
/>

The integration resolves href, type, and inferred sizes before Icons renders. This avoids repeating file-extension logic in every page render and keeps the component surface small.

The icons prop is a Record keyed by each tag’s href. An IconTag value replaces the matching build-time entry (or appends a new one); false removes the matching build-time entry entirely. Entries not mentioned in the prop are passed through unchanged.

Light and dark media values expand automatically

Section titled “Light and dark media values expand automatically”

When a tag uses media: "light" or media: "dark", the component expands that shorthand into the matching prefers-color-scheme query. Other custom media strings are passed through unchanged.

Deduplication is runtime-only and href-based

Section titled “Deduplication is runtime-only and href-based”

Build-time resolution preserves the raw tag array. Icons loads all config tags into an href-keyed map, then applies the icons prop entries — replacing or deleting matching hrefs — so the final render order follows insertion order with prop modifications layered on top.

---
import type { IconTag } from "../integration/generate-icons";
import config from "virtual:eminence-astro-suite/head-tags";
interface Props {
/**
* Late-bound `<link>` tag overrides.
* These are merged over the build-time resolved icon tags from the virtual module.
*/
/**
* Per-href overrides keyed by the tag's `href` value.
* An `IconTag` value replaces the matching build-time tag or adds a new one.
* `false` removes the matching build-time tag from the rendered output.
*/
icons?: Record<string, IconTag | false>;
}
const { icons: overrideIcons } = Astro.props;
const iconTagsMap = new Map<string, IconTag>();
for (const iconTag of config.icons ?? []) {
iconTagsMap.set(iconTag.href, iconTag);
}
for (const [href, iconTag] of Object.entries(overrideIcons ?? {})) {
if (iconTag === false) {
iconTagsMap.delete(href);
} else {
iconTagsMap.set(href, { ...iconTag, href });
}
}
const iconTagsToRender = Array.from(iconTagsMap.values());
const resolveMedia = (media: IconTag["media"]): string | undefined => {
if (media === undefined) {
return undefined;
}
if (media === "light" || media === "dark") {
return `(prefers-color-scheme: ${media})`;
}
return media;
};
---
{
iconTagsToRender.map(({ media, ...iconTag }) => (
<link {...iconTag} media={resolveMedia(media)} />
))
}