Skip to content
HomePagesHomePages template kit
Menu

Add a static asset (image, icon, font)

Add a graphic — an icon, a divider, a photo, a font — to a section.

Add a graphic — an icon, a divider mark, a photo, a custom font — to a section.

A binary asset — an image, a font, a video, an archive — may live in a section’s own sections/<section>/assets/ folder, but only if the section references it: imported in Renderer.tsx (import crest from "./assets/crest.svg") or url()-referenced from one of the section’s CSS files. An unreferenced binary in the folder is flagged; see bundle-binary-asset for the rule and why.

A file shared across sections — a logo, a brand webfont, a texture — lives at the template level instead, referenced the same way with a ../../assets/… path. See Static assets for both paths and the full reference contract. What you reach for depends on what the graphic is:

A vector graphic — an icon, a divider, a decorative mark used only here. Inline it as SVG markup directly in the JSX that draws it. It is text, ships with the rest of your component, and needs no file or import at all:

sections/hero/components/ScrollArrow.tsx
export function ScrollArrow() {
return (
<svg viewBox="0 0 16 16" className="h-4 w-4" fill="none" aria-hidden="true">
<path d="M8 3v10M3 9l5 4 5-4" stroke="currentColor" strokeWidth="1.5" />
</svg>
);
}
sections/hero/Renderer.tsx
import { ScrollArrow } from "./components/ScrollArrow";
// …
<ScrollArrow />

That markup’s bytes ship inside the section’s renderer bundle, so they count toward the same byte budget as the rest of your JSX — see the size stage in Check.

A raster image or watermark used only in this section. Add it under sections/<section>/assets/ and import it into the component:

sections/hero/Renderer.tsx
import { Image } from "@homepages/template-kit";
import crest from "./assets/crest.svg";
export default function Renderer() {
return <Image src={crest} alt="" />;
}

Or reference it from the section’s CSS with url() instead of importing it — see Static assets for both forms. Either way the file must be referenced: an asset nothing imports or url()-references is flagged by bundle-binary-asset.

A photo. Not a section asset at all — a section never ships a photo file. A photo arrives at runtime through an image slot: declare it in schema.ts, produce it with an image-assign decision in fill-spec.ts, and render it through the <Image> primitive, which reads url, alt, and responsive off the slot’s resolved value. See Image slot with a locked crop for the full shape.

A custom font shared across the template. Declared once, for the whole template, in the theme’s fonts and optional fontFaces block. See Theme and CSS. A font only one section uses can instead live in that section’s assets/ folder and be declared with a @font-face in the section’s own CSS — see Static assets.

None — inlining SVG markup and declaring a slot are content edits. No CLI command beyond the author loop below.

Verify with the author loop.

  • Static assets — the full reference/import contract, template level vs. section folder.
  • bundle-binary-asset — the rule that flags an unreferenced binary in a section folder, and the SVG-inlining fix.
  • Check — the size stage a large inlined SVG or a bloated renderer bundle counts against.
  • Image slot with a locked crop — the real channel for a photo: an image slot, an image-assign decision, and the <Image> primitive.
  • Theme and CSS — where a template’s fonts are declared.