Skip to content
HomePagesHomePages template kit
Menu

Image slot with a locked crop

Add an image slot the editor crops to a fixed ratio.

Add an image slot the editor crops to a fixed ratio.

Starting from the starter’s hero section, add a second image slot shaped like hero_image — a crop config, an image-assign decision, an <Image> in the Renderer, and a fixture value.

// schema.ts — a second image slot, same shape as hero_image, own aspect
interior_image: {
type: "image",
editable_by_user: true,
required: true,
produced_by: "interior_image_assign",
display_name: "Interior image",
crop: { mode: "locked", aspect: 4 / 3, aspectLabel: "4:3" },
},
// fill-spec.ts — the decision producing it
{
id: "interior_image_assign",
type: "image-assign",
produces: ["interior_image"],
tag_query: "category:interior",
selection_prompt: "Pick a bright, decluttered interior shot.",
},
// Renderer.tsx — copy the starter's <Image> usage for hero_image
<Image
slotId="interior_image"
src={slots.interior_image.url}
responsive={slots.interior_image.responsive}
alt={slots.interior_image.alt}
className="w-full"
/>
// fixtures.ts — the photo's asset id, exactly as a saved page states it
import { scenarios } from "@homepages/template-kit/fixtures";
const interiorImage = scenarios.luxuryMultiUnit.photos[3]!;
// ...
interior_image: {
photo_id: interiorImage.id,
alt: interiorImage.alt,
},

The url and the responsive ladder the Renderer reads are attached when the fixture loads — see Media is stated by id.

An image slot supports two independent geometry configs — use one, not both, per slot:

  • crop — steers the editor’s cropper only: mode: "locked" fixes an aspect (w/h) and shows the aspectLabel pill; mode: "free" lets the user resize any edge. This is what the recipe above uses.
  • frame — a per-breakpoint aspect (desktop/mobile, switching at a declared breakpoint) that is the single source of truth for BOTH the cropper’s target ratio and the Renderer’s own layout box (imported as a shared constant on both sides — see schema-system.md). Reach for frame instead of crop when the section’s markup itself needs to know the ratio (e.g. to size a wrapper), not just the editor.

Either way, framing is baked into the served asset — the Renderer never applies an aspect ratio to the pixels itself; it only reads url, alt, responsive (and mobile) off the slot’s ImageValue.

None — this is a pure content edit. No CLI command beyond the author loop below.

Verify with the author loop.