Velloo documentation
Markdown for LLMs ↗Repository components
Your app's own components — from its dependencies or its own source — discovered, rendered for real on the canvas, and emitted with their exact imports.
The components in a Velloo screen don’t have to come from a library Velloo adapts. They can be your app’s own: the Mantine or NextUI package it depends on, a private design system, or the components/ directory you wrote last week. Velloo finds them by reading what the app renders, draws them on the canvas from the app’s own installed copy, and emits them with their real import paths.
These are repository components, and they sit alongside the folder’s provider rather than replacing it. A shadcn folder still has shadcn’s Button; an app that also ships its own StatCard gets that too, on its own shelf.
Why this exists
A design tool that only knows five libraries is wrong about every app that isn’t one of them. The old answer — approximate the component with primitives and remember its real name for code emission — kept implementation intent but produced a canvas that didn’t look like the product.
Repository components remove the approximation. The component that renders in the frame is the component that ships.
Discovery
Velloo starts at the app’s entries and routes — Vite’s index.html and src/main.*, Next’s app/**/page and pages/** — follows local imports and barrel files, and records every JSX element it finds whose binding resolves to a direct dependency or to the app’s own source.
Three properties are worth internalizing, because they explain most of what you’ll see:
- It reads text; it never runs your code. Discovery is a source scan. Nothing from the repository executes during it.
- It never walks
node_modules. A package contributes the components your app actually renders from it, not its entire export surface. Install Mantine and use four components, and four components appear. - It leaves an adapter’s own library alone. MUI’s components belong to the MUI adapter, and shadcn’s
ui/directory to the shadcn one, so they aren’t cataloged twice.
Server-only modules are skipped, since they can’t run in a browser. To bound the scan explicitly, hostApp.components.include adds component roots and hostApp.components.exclude drops specifiers or paths; see the config reference.
What Velloo extracts
For each component it finds, Velloo reads the host’s own declarations — a package’s .d.ts, a local .tsx — for a <Name>Props type, literal unions, @default values, and JSDoc. Compound parts (Tabs.List, Tabs.Tab) come from staticComponents and from how the app uses them. Preview states come from Storybook args and from the app’s real call sites, so the Library shows the component with plausible props rather than empty.
Each entry also carries provenance — where in the app it’s used — and, when it reads data for itself, the hooks it calls. That last one matters: a component that pulls from a store or a context won’t be filled by props alone, and the catalog says so instead of leaving you to discover it in a blank frame.
Inference isn’t always right. repo-components.json in the design folder is the checked-in correction layer:
{
"components": {
"@mantine/core#Button": {
"description": "Primary action. Use `variant=\"filled\"` for the main CTA."
},
"./src/components/stat-card#StatCard": {
"states": {
"Loading": { "value": null, "loading": true }
}
},
"./src/components/internal-debug#DebugPanel": { "exclude": true }
}
}
Overrides refine what discovery inferred; they never replace it. A key that no longer matches anything discovered is reported as stale rather than silently ignored.
How a node stores one
A repository component is an ordinary component node carrying a $repo identity:
{
"$ref": "Tabs",
"$repo": { "importPath": "@mantine/core", "exportName": "Tabs" },
"props": { "defaultValue": "overview" },
"children": [
{
"$ref": "Tabs.List",
"$repo": { "importPath": "@mantine/core", "exportName": "Tabs", "member": "List" }
}
]
}
$ref stays the JSX name; $repo carries importPath, exportName, an optional member for a compound part, an optional app in a monorepo, and an optional proxy naming a snippet to draw when the real component can’t render.
Because identity lives on the node, which component renders is never a question of registry order. Mantine’s Button and the provider’s Button can coexist; when two catalog ids would collide, the repo one is qualified (Mantine.Button), and list_components says why.
Compose with them by id, exactly like any other tag:
{
"screenId": "settings",
"mode": "append",
"jsx": "<Tabs defaultValue=\"overview\"><Tabs.List><Tabs.Tab value=\"overview\">Overview</Tabs.Tab></Tabs.List></Tabs>"
}
The preview entry
Your components need the context your app gives them: a MantineProvider, a theme, a query client, a global stylesheet. The preview entry supplies it — preview.tsx in the design folder (or preview.<app>.tsx for a named host app in a monorepo).
It’s a normal module whose default export receives { children, colorScheme, theme, recipeTheme } and returns the wrappers your components need, with the app’s global CSS imported at the top:
import "../src/styles/globals.css";
import { MantineProvider, createTheme } from "@mantine/core";
import "@mantine/core/styles.css";
export default function Preview({ children, colorScheme, recipeTheme }) {
return (
<MantineProvider theme={createTheme(recipeTheme ?? {})} forceColorScheme={colorScheme}>
{children}
</MantineProvider>
);
}
Agents set this up once per folder with two operations:
preview_statusreports whether an entry is in place and proves it by mounting a real component in a headless browser. It returnsstate(absent,valid, orfailing), the wrappers and stylesheets the app’s own entry uses, and asuggestedPreviewEntryto adapt.set_preview_entrywrites the file and re-runs that check, so the answer is a verified mount rather than a guess.
Keep the preview entry free of network calls and credentials. It runs in your local browser during design; treat it as a fixture, not a bootstrap of the real app.
Recipes
For a popular library, Velloo can supply the preview entry itself. A recipe is the small built-in form: the default wrapper and stylesheet imports resolved against your app’s own install, a mapping from Velloo’s tokens to the library’s native theme, design-time adaptations that keep overlays inside the frame, the style props its components accept, and a stylesheet probe.
Mantine is the first recipe. A Mantine app gets a working canvas with no preview.tsx at all; write one when you want the app’s own theme, router, or data providers instead.
A recipe is deliberately not an adapter. An adapter owns styling, theme projection, and codegen idiom for a library Velloo knows deeply; a recipe just supplies context so the app’s own copies render. Most libraries need the second and not the first.
Fidelity is reported, never assumed
Each repository component falls back on its own. One component that can’t render leaves the rest of the screen mounted and real — it alone becomes its proxy snippet or a labelled frame.
component_status reports each one with a status and a stable code:
| Status | Meaning |
|---|---|
exact | The app’s own component, rendered from its own install |
adapted | Rendered with a design-time adaptation (an overlay kept inside the frame) |
unstyled | It rendered, but its stylesheet wasn’t loaded — so the result can’t be called exact |
proxy | It couldn’t render; the snippet named in $repo.proxy stands in |
unavailable | It couldn’t render and has no proxy; a labelled frame stands in |
When something isn’t exact, a code says why — missing-provider, render-threw, missing-export, compile-failed, server-only, resolve-failed, unstyled — and a remedy says what to do about it. missing-provider almost always means the preview entry needs another wrapper.
The unstyled case is worth calling out: a component that renders without its CSS looks like it worked. A recipe’s stylesheet probe is a DOM check that fails when the library’s CSS is absent, which is what turns a false exact into an honest unstyled.
Emit
Codegen prints a repository component as itself: its authored props, its children, and its exact import via repoImports. Nothing is translated into another styling system, because nothing needs to be — the component in the design is already the component in the app.
When to reach for something else
- A snippet is still the right tool for a composition you want to reuse across screens and edit in one place. It’s a Velloo-side abstraction, not an app component.
- An extension remains the opt-in path for a dynamic leaf that can’t render as a normal node — a chart wired to a live data layer — where you want a bundled visual island rather than a tree of nodes.
$emitAsstill loads on existing folders, and the port-a-page flow still uses it where a preview is deliberately approximate. For an app component that can simply render, prefer the repository catalog.
Trust boundary
Repository code runs only in your local browser, inside the canvas mount. Publishing uploads preview PNGs captured on your machine; Velloo Cloud renders the design JSON with proxies and never executes your application code.
Next
- The node forms in full: Screens and nodes.
- How the folder’s provider and styling idiom are chosen: Framework-native.
- The discovery and preview fields in config: Config reference.