Skip to main content
Register a component and a generated screen can name it. What you register is a closed contract, so a name a screen renders is a name your product really has.

Register your components

One object, keyed by component name, under one name at both ends: createVendo({ components }) on the server and <VendoProvider components> in the browser take the same object.
vendo.ts
  • description is selection guidance. Say what the component shows and when to reach for it.
  • props validates at render and derives the model-facing JSON Schema. One schema, never two.
  • examples are JSON prop objects the model copies from, and they draw the console’s preview.
Copy prop names from the component’s source, never from convention. If SpendingDonut takes slices, the schema says slices, not data.Prop invention is the single most observed agent failure, and a schema is what makes it impossible.
Registering a component lets a screen render it. Letting a user change one is a different wrapper, in import & fork.

What a screen may name

A name outside these two sets fails the save. It never renders as an empty box.

The Kit is one family, and every component in it reads your theme tokens. There is no value-formatting tier: format a figure with Intl in the screen’s own code and hand the result to a <Text>. There is no Skeleton either, and DataTable is the only table. It sorts, filters, searches, paginates, resolves dot-path keys, and formats each cell.

What vendo sync captures

Two artifacts, both deterministic and byte-stable. Commit them.

catalog.json is what your components are called. .vendo/components/ is what they are.

catalog.json is what your components are called: names, prop schemas, descriptions, examples. A re-scan keeps description and example copy you edited by hand. components/ is what they are. Source is stored once per module and referenced by content hash, so a format-currency.ts that ten components import is stored once. The .vendo directory has every path.

Where the walk stops

The import walk follows your own code to closure with no depth limit, and stops at exactly two lines: anything resolving into node_modules, and 256 KB of source per component across the whole closure. Three packages are bundled into the sandbox itself, because they blocked almost everything and are tiny.
The bundled zod is a shim, not zod. It resolves the declaration surface so your module loads, which is all a registry’s props: schema needs.It does not validate. .parse() and .safeParse() throw a named ZodShimError rather than returning a plausible wrong value.

When a component cannot be captured

Sync never goes quiet. The record on disk carries a machine-readable skipped.reason plus a sentence you can read. A component that could not be read at all this run is a different case. Sync leaves the previous capture exactly as it was and moves on.

How a preview draws

A preview has no data plane, so every query resolves to an empty list. Sync fills it from what you have already written, down three rungs.
1

Your examples

The first examples string that parses to a JSON object wins. A human’s example is real product data, and it reads better than anything Vendo could invent.
2

Generated from your props schema

Values synthesized from the schema: typed correctly, respecting enums, min, max, string formats, and optionality. Plausible rather than pretty.
3

An honest label

With neither, the capture records noSampleProps with a reason. The component still renders in your product, and previews as a labeled placeholder.
Generated values are seeded from the component’s name and each property path, so the same schema always produces the same values and your committed .vendo/components/ never churns. Sample props travel with the capture, so keep them realistic and non-sensitive.

Sending the corpus to Cloud

Sync asks once per project whether your component source may go to Vendo Cloud, so the console can render your real components instead of grey placeholders. It commits your answer.
.vendo/cloud.json
In CI, pass --push-components or --no-push-components. The CLI reference has every sync flag.