- Button
- Date Picker
- Field
- Fields
- Input Group
- File Uploader
- Signature Pad
- Text Area
- Container
- Section Header
- Separator
- Spacer
- Action Row
- Breadcrumb
- Tabs
- Tabs Container
- Card
- Image
- Text
- Title
- Video Player
- Card Collection
- Data Grid
- List Collection
- Table Collection
- Bar Chart
- Big Number
- Line Chart
- Radial Chart
- Hint
- Progress
- Spinner
- Skeleton
- Toast
- Sheet
- Dialog
- Real-world patterns
- Primitives
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:buildThis 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 devNavigate 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-webThe 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
- Edit the source file in
registry/(not inpackages/ui-webdirectly) - Rebuild:
pnpm --filter=@pex/ui registry:build - 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.