Skip to content
HomePagesHomePages template kit
Menu

template-kit/size-renderer-bundle

The template-kit/size-renderer-bundle authoring rule — what it enforces, why, and how to fix it.

A section’s renderer bundle must be ≤ 50 KB gzip. The budget is not author-overridable.

Each section’s renderer is bundled with esbuild and minified, with React treated as external (it is a peer, shared by the whole page, and is not counted against any one section). The gzipped result must be 50 KB or less.

The budget is visible in template-kit check’s configuration and is not author-overridable. A template workspace cannot raise it, and there is no per-section exemption. If you go looking for the flag, this is the answer: there isn’t one.

The output is a real page for a real buyer, standing in a driveway on a phone. Bytes are the one cost the section cannot delegate.

The number is set to be generous — roughly twice the largest section shipping today — so it does not fight ordinary authoring. You will not hit it by writing a rich section. You hit it when something runaway gets pulled in:

  • a barrel import of an icon set, which drags every icon into the bundle so you can use three;
  • a package that brings a font, a locale table, or a polyfill along with it, none of which you asked for.

That is the shape of essentially every violation: not a section that grew, but a single import that was more expensive than it looked.

Find the import that is not paying for itself.

  • Import named symbols, never a namespace. import { ArrowRight } from "…" is tree-shakeable; import * as Icons from "…" is not — a namespace object has to contain everything, so nothing can be dropped.
  • Prefer the package’s narrow entry if it offers one.
  • Hand-write the residue. If you use one function out of a large utility package, copy the function.

Do not try to hoist code out of the section to dodge the budget. Each template owns its own presentation code; there is no shared library above a section to hoist it into, and the bytes would still land on the same page.

sections/amenities/Renderer.tsx
import { Section, Slot, SlotItem } from "@homepages/template-kit";
import * as Icons from "lucide-react"; // ← the whole set: every icon, in the bundle
import type { Props } from "./schema.js";
export function Renderer({ slots }: Props) {
return (
<Section>
<Slot id="amenities">
{slots.amenities.map((item, i) => (
<SlotItem key={typeof item.id === "string" ? item.id : i} index={i} as="li">
<Icons.Check aria-hidden />
<h3>{typeof item.label === "string" ? item.label : null}</h3>
</SlotItem>
))}
</Slot>
</Section>
);
}
sections/amenities/Renderer.tsx
import { Section, Slot, SlotItem } from "@homepages/template-kit";
import { Check } from "lucide-react"; // ← one icon, and the bundler can drop the rest
import type { Props } from "./schema.js";
export function Renderer({ slots }: Props) {
return (
<Section>
<Slot id="amenities">
{slots.amenities.map((item, i) => (
<SlotItem key={typeof item.id === "string" ? item.id : i} index={i} as="li">
<Check aria-hidden />
<h3>{typeof item.label === "string" ? item.label : null}</h3>
</SlotItem>
))}
</Slot>
</Section>
);
}
  • size-section-css — the same budget on the other half of what the section ships.
  • license-denied — the other reason to look hard at a dependency before you take it.