Studio Docs

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.

Do It With a Skill

compose-marketing-section assembles this from already-registered primitives. For the repeating half specifically, build-repeating-section builds the Repeater plus Section Slot pairing.

curl -fsSL https://studio-documentation.contentstackapps.com/install.sh | sh

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.

Studio's Data Picker open on the right panel, showing the Blogs content-type schema tree: Title, URL, Excerpt, Featured Image (with nested fields for filename, url, dimensions), Primary Author (reference), Co-Authors (reference multi), Content Tier (choice), Related Blogs (reference multi). Author clicks any leaf to bind the current prop to that field.

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.

A Repeater node selected in a Card Grid section. Right panel shows Configuration with Preview Mode toggled on, and Properties with Contents (a Condition Block) and Items (bound to a Related Posts multi-valued field). Canvas renders two iteration cards populated with real titles from the previewed entry's related-posts list.

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

A Feature Grid section: a centered section intro (H2 heading + muted description) followed by a 3-column responsive grid of three Cards. Each Card has an icon Image at the top of its header slot, then a small H3 heading, then a muted description below. Cards use the default shadcn variant with a subtle border and shadow.

  • 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 UIDTypeNotes
features_section_titleSingle-line textSection intro heading
features_section_descriptionMulti-line textSection intro description
featuresReference (multi) to featureThe list the Grid repeats over

feature: one entry per feature:

Field UIDTypeNotes
iconFile (asset)Icon image for the card header
titleSingle-line textCard heading
descriptionMulti-line textCard 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

  1. Create the feature Content Type with fields icon, title, description.
  2. Add a features Reference-multi field to landing_page, targeting feature.
  3. Create 3+ feature entries with real content: icons, titles, descriptions.
  4. Open the Sections palette, click Create Section, name it Feature Grid — 3-column.
  5. Set the linked schema to landing_page.
  6. Drag Section onto the canvas. Configure spacing: spacious, contentAlign: center.
  7. Drag the outer Stack into Section’s children. Configure spacing: loose, alignment: center.
  8. 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).
  9. Drag Grid as a sibling of the intro Stack. Configure columns: 3, spacing: normal.
  10. Convert Grid into a Repeater: right-click the Grid node, “Bind list to…”, select landing_page.features. Grid becomes a Repeater with one iteration.
  11. Drag Card into the Repeater’s iteration. Configure variant: default, padding: normal.
  12. 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).
  13. In Card’s content: drop Description (emphasis: muted, bind text to feature.description).
  14. 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

  1. Fill a landing_page entry with features_section_title, features_section_description, and 3 feature references.
  2. Drop Feature Grid — 3-column on a Template with URL /landing/:slug.
  3. Visit /landing/<slug>. The Grid renders 3 Cards with the referenced feature entries.
  4. Add a 4th feature entry to the reference list, publish, refresh. The Grid picks up the new card without a schema change.