Contributing

PreviousNext

How to add and document new components

Adding a Component to the Registry

The registry (apps/ui/registry/) is the source of truth for components. New components are developed here, documented in the docs site, then promoted to packages/ui-web when stable.

1. Create the component file

Place the component in the appropriate subdirectory of registry/:

registry/ui/          ← shadcn primitives (button, input, card, etc.)
registry/forms/       ← form controls
registry/layout/      ← layout utilities
registry/navigation/  ← tabs, breadcrumbs, navbars
registry/charts/      ← data visualisations
registry/collections/ ← data grids, tables, lists
registry/feedback/    ← spinners, progress, toast
registry/hooks/       ← custom hooks

2. Declare it in the registry

Add an entry to the matching declaration file (registry/registry-ui.ts, registry/registry-hooks.ts, etc.):

{
  name: "my-component",
  type: "registry:ui",
  dependencies: ["class-variance-authority"],
  registryDependencies: [],   // other registry items this depends on
  files: [{ path: "ui/my-component.tsx", type: "registry:ui" }],
}

3. Rebuild the registry

pnpm --filter=@pex/ui registry:build

This regenerates registry/__index__.tsx (used for live previews) and public/r/*.json (installable by consumers via shadcn add).

4. Create the docs page

Create an MDX file under content/docs/ui-library/components/[category]/my-component.mdx and add its path to content/docs/ui-library/meta.json:

---
title: My Component
description: What this component does
---
 
import { UILibPreview } from "@/components/ui-lib-preview";
import { MyComponentExample } from "@/components/ui-library-examples/[category]/my-component-examples";
 
## Basic Usage
 
<UILibPreview code={`// Component is in the registry — run sync:to-ui-web before publishing this import path
import { MyComponent } from "@pex/ui-web/[category]/my-component";
 
<MyComponent />`}>
  <MyComponentExample />
</UILibPreview>

Create the example component at components/ui-library-examples/[category]/my-component-examples.tsx — this is what renders in the live preview. Update the code snippet to remove the comment once the component has been promoted to packages/ui-web.

5. Preview

pnpm --filter=@pex/ui dev

Navigate to http://localhost:3012/docs/ui-library/components/[category]/my-component.


Promoting a Component to packages/ui-web

Once a component is stable and other apps need it, sync it using the promotion script:

# Dry run — see what would be copied
pnpm --filter=@pex/ui sync:to-ui-web --dry-run
 
# Sync a specific component
pnpm --filter=@pex/ui sync:to-ui-web --component my-component
 
# Sync all registered components
pnpm --filter=@pex/ui sync:to-ui-web

The script rewrites imports and updates packages/ui-web/src/index.ts automatically. Only components declared in the registry declaration files are eligible.


Updating an Existing Component

  1. Edit the source file in registry/ (not in packages/ui-web directly)
  2. Rebuild: pnpm --filter=@pex/ui registry:build
  3. When ready to ship: run sync:to-ui-web --component <name>

When shadcn Regenerates a Component

shadcn components are vendored — nothing regenerates them automatically. If you deliberately re-pull one from the CLI, the generated file will use shadcn defaults (rounded-md, shadow-md, etc.). Diff against our version and restore the PEx radius conventions:

  • rounded-4xl — standalone pill controls (Button, Input, Select trigger, Toggle, Badge)
  • rounded-2xl — floating surfaces and cards (DropdownMenu, Popover, Select content, Dialog, Card)
  • rounded-xl — grouped controls and menu items

See "Border radius" in apps/ui/CLAUDE.md for the full reference.