Contributing

How to work inside the nx-ui repo itself — the source of the registry, the showcase and the agent skills. AGENTS.md is the single source of truth; this is the orientation.

The verify loop

One command is the whole gate — typecheck, then registry build, then the app build. CI runs exactly this:

shell
bun run check
  • bun run check = tsc --noEmit + registry:build + next build. A stale registry.json or public/r/ fails the build rather than silently drifting.
  • bun run smoke — the interaction smoke test: headless Chromium clicks every button on every showcase page (needs bun run dev running). It catches popup-mount errors that builds cannot — e.g. a Base UI menu label placed outside a Group. Run it after touching menus, dialogs or blocks.
  • bun run registry:build — regenerates public/r/*.json from registry.json. Run it after touching components/, and commit the regenerated output — consumers install from those files.

Rebuild the registry after touching components

After changing any file under components/ or registry.json, run bun run registry:build and commit the regenerated public/r/ output. That committed JSON is the artefact raw.githubusercontent.com actually serves — git push is the deploy.

The minimal-diff doctrine

Components stay as close to the upstream shadcn base-vega sources as possible — upstream diffability is a feature. Restyling happens through the theme first, class deltas second. The permitted deltas:

  • Tokens do most of the work — the layer-2 mapping restyles components for free.
  • Shadows, focus ring, type, radii follow the UUI system (controls get shadow-xs; a 4px brand-tinted focus ring; control labels font-semibold; controls rounded-lg, cards rounded-xl, pills rounded-full).
  • Icons swap lucide-react @untitledui/icons equivalents.
  • Never changea component's exports, props, Base UI part structure, data-attributes or file/export names — a consumer following shadcn docs must not be surprised.
The full record of the design decisions lives in docs/architecture.md. Read it before changing components, tokens or registry structure. Conventions: kebab-case files, British English, one component per file, no barrel-file re-export mazes.

How demos, pages and nav are generated

bun run registry:build chains three steps, so a new component or page self-registers:

shell
bun run registry:build   # build-registry.ts → shadcn build → nav:build
  1. scripts/build-registry.ts scans the repo and generates registry.json — one item per file in components/ui/, components/ai/ and components/blocks/, plus hooks, lib modules, datasets and the theme items. Per-item dependencies are derived by parsing each file's own imports, so the registry never drifts out of sync with the source.
  2. bunx shadcn build reads that registry.json, inlines each file's contents, and writes one flattened JSON per item into public/r/.
  3. scripts/build-nav.ts (nav:build) scans the route sections under app/(showcase)/ and regenerates the nav manifest. Every section directory with per-item page.tsx subdirectories becomes a nav section; titles and descriptions resolve from registry.json, then an optional section _meta.json, then a Title-Cased slug.
To add a component: drop the file in components/ui/, add a demo page under app/(showcase)/components/<name>/page.tsx, run bun run check, and commit the regenerated public/r/ and nav manifest. No nav wiring by hand.
Back to the documentation index, or read Getting started to see the consumer side of the same registry.