Skip to content

Authoring a component

Nik edited this page Aug 5, 2026 · 1 revision

Authoring a component

The authoritative instructions live in the repo and are kept current there:

  • CONTRIBUTING.md — setup, "Adding a component", framing the card, the token rule, the quality gate, working in parallel, pull requests, DCO sign-off.
  • AGENTS.md — the same ground at more depth for an agent editing the repo, plus the autoplay descriptor schema, driver constraints, useWhen, and prop-signature extraction.

This page covers the shape and the traps, and links rather than restating.

The folder

registry/{core,loud}/<name>/:

File Role
component.tsx The component. "use client", zero or minimal dependencies, tokens not hex. This is the file that gets installed, and the file the prop-signature scanner and the cssVars scanner both read.
demo.tsx The preview render used by /components/<name> and by the /preview/<name> screenshot fixture. Its presence is what makes the folder registrable at all — build-index.ts skips a folder with no demo.tsx.
meta.json The single source of truth for everything published about the component.
screenshots/ Generated by npm run verify. Not hand-authored. Committed, so the gate's claims are auditable.

Registration is automatic. There is no list to add the component to.

meta.json fields

Observed across the sidecars and consumed by build-registry.ts, build-llms.ts and build-autoplay.ts:

  • name — must equal the folder name. build-registry.ts keys items by meta.name while build-index.ts keys demos by folder name, so a mismatch publishes an installable payload whose preview has no demo. Currently a loud warning, not a build failure.
  • title, description — catalog copy.
  • collection — core or loud. Decided at creation time; it drives folder placement, not a runtime flag.
  • tags — searchable, and feed categorisation.
  • instruction — a rich paragraph describing the behaviour, read by build-llms.ts into llms-full.txt. This is the hand-written text that lets a model tell apart two components sharing a UI role.
  • useWhen — optional override for llms.txt's "use when" line, for disambiguating components that share a role. See the useWhen section of AGENTS.md.
  • dependencies — npm packages the shadcn CLI installs alongside the source.
  • autoplay — optional, site-only. Makes the landing-page card demonstrate itself. Schema and driver constraints are in AGENTS.md.
  • card — optional, site-only framing (focus selector, padding). See "Framing the card" in CONTRIBUTING.md.

The token rule

Colours come from CSS custom properties already in scope, never a hardcoded hex — in markup and in <canvas> / SVG draw code. If a component derives ink for a canvas, read it via getComputedStyle at mount and on theme change rather than baking in a literal. Hardcoded colour broke the light theme across the whole registry once.

The token names are --background, --foreground, --border, --ns-muted, --ns-accent, --ns-accent-hover, --surface, --error, --warning, --success. --muted and --accent no longer exist — they were renamed to --ns-muted / --ns-accent, and a bare --muted in new code resolves to nothing at all. scripts/test-source-invariants.ts gates this.

Which of those tokens the registry ships to a consumer and which inherit from their theme is on Installing a component. Both themes must render correctly — verify both.

Before opening a PR

npm run registry:build   # regenerate everything from the sidecar
npm run typecheck        # tsc --noEmit, must be clean
npm run verify           # the Playwright gate, with `npm run dev` in another shell

Then the gates.

Two things that catch people

Generated files never go in a diff. See "Generated files never go in a diff" in CONTRIBUTING.md and the Architecture page. If a generated file conflicts on merge, regenerate — do not hand-merge.

Run npm run order:build after adding components, and commit lib/component-order.json. It is deliberately outside the registry:build chain. Forgetting is easy to miss and backwards: an unranked component sorts last under the catalog's "Newest" sort, so the component you just built is the hardest one to find. This has happened twice, at 16 and then 22 components.

Submitting without cloning

There is a submission portal at /submit on the live site, backed by an incremental GitHub OAuth handshake that forks the repo and opens a pull request. It has its own environment requirements (see the /submit section of AGENTS.md and .env.example).

Clone this wiki locally