Skip to main content
A component presents one thing: a card showing a product, a form, a chart. A template holds components, and both are streamed into an app, which opens the place each one’s external state names. Folders, manifests and the toolchain work the same way for every artifact, and Essentials covers them. This page is what makes a component a component.

The anatomy

A complete component in four files: a product card that shows as a compact tile, and opens into a full page.
Four things in those files carry the whole model:
  • The envelope declares the faces. grid and page are the two states this card can be in. The first declared is where it arrives.
  • Every state names its layout. layout: layouts/grid is a path, so nothing is assumed from the state’s name.
  • The card opens itself. The tile is a Button whose action writes state: page into the card’s own data, and the page writes it back. Each state may name its place:, so the card moves from the rail to the main panel and back.
  • Prop defaults are the preview. studio draws the card from them before any workflow exists, so write realistic content.
A component that draws one thing needs none of this. One file with a root: tree, no states and no manifest, is a complete component. Two tiers. Your components live in components/ at the root of your org repo and belong to that org alone. Two orgs may each have a component of the same name, because the org is part of what an item is. The design system’s are shared by every org. Yours may reference a design-system component, never the reverse, and may never take a design-system name. Both are lint errors.

Where each thing lives

Everything a component shows has exactly one home, and the lint rejects anything in the wrong one: Declare in props every field the outside can fill, and mark each one input: true: those are the fields on the component’s tool. A prop marked input: false keeps its default and is never on the tool. A prop typed unoverse-markdown is a body: the platform compiles the document’s typed components onto that field, from the atoms in the markdown category, so the prop’s description is the whole instruction for the body. Any prop can take the type, whatever fills it. Bind it to a Markdown primitive by name. An array, an object or a URL in values: is the tell for a mistake. That is content to hardcode, or data to declare as a prop. Anything computed is computed in the workflow and arrives as a plain field. Prop names are the data contract. Source data fills a component by name, with no mapping layer. A bind whose name the source does not carry renders the preview default instead. That failure is quiet and specific: the title streams in while the image and tagline stay on their mocks. Rename the prop to match the source field, and never add glue. A component fed by your content binds to a fixed set of field names. Interface data lists them, and names which of them arrive with the delivery and which are fetched when a detail state opens. A component can also fetch its own data at a declared moment, such as a page hydrating when it opens. Lifecycle hooks is the one code carve-out.

States

A component with more than one arrangement declares a state tree. External states sit at the top level, and internal steps nest inside them:
Two decisions are yours. Which states are external, since those are all the app ever sees. And what to call them: grid for the compact face and page for the full detail face are the standard names, so a card using them matches any template, in any org, with no mapping. One question settles the first decision:
If the screen must rearrange to show it, make it external. If nothing would move, nest it.
The same product is a compact tile in grid and a full page in page. Both move the screen, so both are external. Stepping from detail to apply inside that page moves nothing, so it nests. State is the full model: what writes a state, how everything else reacts, and how to model a tree well.

Describing a field

Describe what a field is on its prop, the same way you would document any parameter. The prop is the field’s whole contract: what it is, whether the outside fills it, and its limits.
input decides who fills it. input: true puts the field on the component’s tool, so an Agent or a workflow writes it. input: false keeps it the component’s own, drawn from its default. That is the only switch: there is no separate writer role and nothing on the layout grants it. Two things come from writing that description well:
  • Any Agent using the component knows what belongs in the field.
  • unoverse can fill the field for you. An Agent writes the content from your description, drawing on search results rather than inventing anything.
Constraints sit beside the description. Text takes maxLength. A list says the count it should hold, and describes its items the same way:
The platform compiles every prop into the component’s tool schema, so there is no prompt to maintain anywhere. Change a description or a count, and behaviour changes on the next render.

How an Agent finds it

Your manifest.yaml carries the four fields that decide whether this is ever chosen: title, description, whenToUse and category. The rules are identical for every kind, they are enforced by the deploy lint, and Node discoverability is the contract.

A component that asks

A form or wizard that hands answers back to the Agent needs two things, and nothing else:
The platform does the rest. When an Agent calls the component, it waits; the component’s submit sends back exactly the outputs keys from its own data, and the Agent carries on with them. The component then steps aside to its card in the conversation, where the person can reopen it. A ✕ that should stop the question writes type: cancel, and the Agent hears that the person cancelled. Steps, tabs and states in between answer nothing. Only submit and cancel do. This is native MCP elicitation, so a host like ChatGPT shows its own form from the same outputs.

Next steps

State

The full reaction contract between a component and whatever holds it.

manifest.yaml

Every field a component can declare, with its type.