Skip to main content
An interface is a component and its data, delivered together. When that data comes from your content, its shape is fixed, and these are the field names to bind to.

Where it comes from

Ingesting a site or a document writes rows into spatial. One row is one item: a product, a service, a page, an article. The rows are the only content truth, and the sections tree you curate during an import is derived from them, which is why a row carries the section it came from. A search returns rows. A row that names a component arrives on screen as an interface, already filled. Content data arrives in two layers. Layer 1 comes with the delivery, and every card is drawn from it. Layer 2 is the long-form record behind the card, fetched only when something opens.

Which component a row renders as

A row carries an app field naming the component or app it should render as, such as acme/product-card. Anything that finds the row renders what it names, and the row’s fields fill that component’s props. That field is the join between your content and your design. Assign it once per row, or in bulk across a section, and every future delivery of that row arrives wearing the right component. A row without one is data an Agent can talk about, rather than an interface it can show.

Layer 1: what arrives with the card

Every row carries these, whatever kind of item it is: object_type is the field to know. Ingesting a page extracts need rows, which carry one editorial field between them. Promoting a page writes a service row, which is the authored content item a card is built from, and it carries the full set: Rows of other kinds live in spatial too, such as image, skill and mcp. They are not content items, and they do not fill a card. Images take one detour worth knowing about. A model never receives image URLs, so a row reaching an Agent carries only a hasImage flag, and the server fills primaryImage and images on the way to the card. You still bind to them by name as usual, and authored artwork reaches the screen without passing through a model.

Layer 2: what getDetail adds

A search result is a summary. The full record stays in the dictionary until a component asks for it, and a component asks by declaring the getDetail hook on the state that needs it (Lifecycle hooks). The layer 1 editorial fields arrive again with layer 2, so a detail view has the whole record in one place. Fields outside these lists never reach a card. Crawler bookkeeping such as timestamps, paths and structured markup is not content, and a renderer that never sees it cannot leak it.

Bind by name

A row fills a component by name, with no mapping layer anywhere. Name a prop tagline and the row’s tagline fills it. Name it subtitle and nothing arrives, because no row carries that word.
The failure is quiet, so it is worth recognising: a card whose title streams in while its image and tagline sit on their preview defaults has a prop name the row does not carry. Rename the prop to the field, and never add a mapping layer.

Next steps

Lifecycle hooks

Declare the hook that fetches layer 2 when a detail state opens.

Components

Build the card these fields fill.