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 a catalog

One object, keyed by component name. createVendo reads it as catalog, and <VendoProvider> reads the same object as components.
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. A schema is what makes it impossible.

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. Accordion is the one Kit name a generated screen may not reach for, because its panels arrive as element-valued props. Tabs takes its panels as children, so it stays available.
Two names people look for are deliberately absent. DataTable is the only table, and it sorts, filters, searches, paginates, resolves dot-path keys, and formats each cell.There is no Skeleton either. A loading placeholder is chrome the renderer paints while an app streams in.

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.

.vendo/catalog.json records what your components are called: the module that exports each one, its JSON Schema props, its description, and its examples. Rescans preserve description and example copy you edited by hand. .vendo/components/<Name>.json records what they are. It holds no source of its own, only the module that declares the component, the binding to render, a content hash, and references. Every byte lives beside it in .vendo/components/modules/<hex>.json as { source, imports? }, keyed by the sha-256 of its own content. A format-currency.ts that ten components import is stored once and referenced ten times.

Where the walk stops

The import walk follows your own code to closure, with no depth limit. It stops at exactly two lines.
  • The package boundary. Anything resolving into node_modules is never captured, because it is not your code.
  • A byte budget. 256 KB of source per component, counting 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 solves that from things you have already written, resolving a seed 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.
Generation is seeded from the component’s name and each property path, so the same schema always produces the same values. Your committed .vendo/components/ never churns. Examples and generated values both travel with the capture. Treat them as you would any other value that crosses the wire, and use realistic but non-sensitive data.

Sending the corpus to Cloud

Sync offers to push the corpus so the console can render your components instead of grey placeholders. It asks once per project and commits the answer to .vendo/cloud.json.
.vendo/cloud.json
In CI, pass --push-components or --no-push-components. A non-interactive run with no saved answer and no flag pushes nothing and says so. The push is cheap by construction. Component records carry references rather than source, so listing them is the hash manifest, and one keys-only call answers which module bodies the console already has.

Where to go next

Import & fork

Let a user fork one of your components and make it theirs.

In-client venue & approvals

Approve a version and it runs as real code in your page.