Compose a Feature Grid from primitives
A three-column feature-callout section (icon + heading + description per card) composed from the same primitives as the Heroes. The Grid becomes a Repeater. Each Card iteration binds to one feature entry.
New to Studio's smart containers? Repeater iterates a bound list, rendering one iteration per item. This recipe wraps a Grid in one. The columns: 3 renders three cards, one per feature entry, automatically.
How binding works in Studio's Data Picker
Every atom prop's binding (Heading.text, Image.src, Button.href) is set via Studio's Data Picker, a right-panel view of the linked Content Type's schema tree. Click the small chip next to a prop, pick the field. The prop resolves to that field's value at render time.
Inside a Repeater, the Data Picker scope shifts to the iteration item. When you bind a prop on a component inside a Repeater's slot, the picker shows the fields of ONE feature entry (not the parent Content Type). This is what makes the composition tree above work: the Card's Heading.text binds to feature.title, not to landing_page.features[0].title. The Repeater handles iteration for you.
Once the Repeater's Items field is bound and Preview Mode is on, the canvas renders one card per entry in the list. Studio handles the iteration. You compose the one card template inside the slot.
Prerequisite reading. Design a component library that composes, not sprawls: the 10 primitives this recipe uses.
What you'll build
- One Section with a centered intro
- One Grid (3 columns) that acts as a Repeater over a Reference-multi field
- Three Card iterations, each rendered from a feature entry with icon, heading, and description
Prerequisite: the Content Type shape
Two Content Types:
landing_page: the page CT, with:
| Field UID | Type | Notes |
|---|---|---|
| features_section_title | Single-line text | Section intro heading |
| features_section_description | Multi-line text | Section intro description |
| features | Reference (multi) to feature | The list the Grid repeats over |
feature: one entry per feature:
| Field UID | Type | Notes |
|---|---|---|
| icon | File (asset) | Icon image for the card header |
| title | Single-line text | Card heading |
| description | Multi-line text | Card body |
Composition tree
Section (spacing: spacious, contentAlign: center, background: none)
└── Stack (spacing: loose, alignment: center)
├── Stack (spacing: normal, alignment: center) ← section intro
│ ├── Heading (text: "Everything you need to ship", level: h2)
│ └── Description (text: "Built for teams that ship weekly.", emphasis: muted)
└── Grid (columns: 3, spacing: normal) ← Repeater over `features`
└── Card (variant: default, padding: normal) ← one iteration per feature entry
├── header:
│ └── Stack (spacing: tight, alignment: left)
│ ├── Image (src: <icon>, aspect: 1:1, fit: contain)
│ └── Heading (text: <title>, level: h3)
└── content:
└── Description (text: <description>, emphasis: muted)Bindings
Section-level (static, set once per Section, not per entry):
- Section.spacing: spacious
- Section.contentAlign: center
- Grid.columns: 3
- Grid.spacing: normal
- Card.variant: default
- Card.padding: normal
Repeated per feature (the Grid is a Repeater over landing_page.features):
- Section intro Heading.text binds to landing_page.features_section_title
- Section intro Description.text binds to landing_page.features_section_description
- Per-card Image.src binds to feature.icon.url
- Per-card Heading.text binds to feature.title
- Per-card Description.text binds to feature.description
Varieties from the same tree
- 4-up grid: change Grid.columns: 3 → 4. No other change.
- Outlined cards: change Card.variant: default → outline for a lighter treatment on white backgrounds.
- Ghost cards: change Card.variant: ghost and Section.background: muted for cards that read as part of the section rather than as elevated tiles.
- Add a CTA link per card: drop a Button (variant: link, icon: arrow-right) into each Card's footer slot with a descriptive label per feature (e.g. "Read the case study", "View integration guide"). Bind label and href to two new fields on the feature CT.
- Two-line grid: change Grid.columns: 3 with more than 3 features. Grid wraps to a second row automatically.
Which layout props to expose as Section-level
- Good Exposed Props: Grid.columns, Section.background, Card.variant. Template authors can pick a layout treatment per Template drop without a code change.
- Never Exposed: the atom props. Content flows through the CT. Visual polish is design-system-locked.
Building this in Studio: step by step
- Create the feature Content Type with fields icon, title, description.
- Add a features Reference-multi field to landing_page, targeting feature.
- Create 3+ feature entries with real content: icons, titles, descriptions.
- Open the Sections palette, click Create Section, name it Feature Grid — 3-column.
- Set the linked schema to landing_page.
- Drag Section onto the canvas. Configure spacing: spacious, contentAlign: center.
- Drag the outer Stack into Section's children. Configure spacing: loose, alignment: center.
- Drag an inner Stack for the intro. Drop Heading (level: h2, bind text to features_section_title) and Description (emphasis: muted, bind text to features_section_description).
- Drag Grid as a sibling of the intro Stack. Configure columns: 3, spacing: normal.
- Convert Grid into a Repeater: right-click the Grid node, "Bind list to", select landing_page.features. Grid becomes a Repeater with one iteration.
- Drag Card into the Repeater's iteration. Configure variant: default, padding: normal.
- In Card's header: drop a Stack, then Image (bind src to feature.icon.url, the Repeater scope makes feature.* available) and Heading (level: h3, bind text to feature.title).
- In Card's content: drop Description (emphasis: muted, bind text to feature.description).
- Save the Section. Preview: with 3 feature entries, the Grid renders 3 Cards. With 6 entries, it renders 6 Cards wrapped to 2 rows.
Testing
- Fill a landing_page entry with features_section_title, features_section_description, and 3 feature references.
- Drop Feature Grid — 3-column on a Template with URL /landing/:slug.
- Visit /landing/<slug>. The Grid renders 3 Cards with the referenced feature entries.
- Add a 4th feature entry to the reference list, publish, refresh. The Grid picks up the new card without a schema change.
- Hero from primitives
- Testimonial cards from primitives
- Pricing tiles from primitives
- Card grid with slots: for the more advanced pattern where each card's contents vary per-Template