Contributing
This guide explains how to add new components to the Mukoko Registry. Every component must follow the established patterns to maintain consistency across the ecosystem.
Adding a new component
1. Create the component file
Create your component in components/ui/:
// components/ui/my-component.tsx
"use client"
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const myComponentVariants = cva(
"inline-flex items-center justify-center rounded-md transition-colors",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground",
outline: "border border-border bg-transparent text-foreground",
},
size: {
default: "h-10 px-4",
sm: "h-8 px-3 text-sm",
lg: "h-12 px-6 text-lg",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
interface MyComponentProps
extends React.ComponentProps<"div">,
VariantProps<typeof myComponentVariants> {}
function MyComponent({ className, variant, size, ...props }: MyComponentProps) {
return (
<div
data-slot="my-component"
className={cn(myComponentVariants({ variant, size, className }))}
{...props}
/>
)
}
export { MyComponent, myComponentVariants }2. Follow the mandatory patterns
Every component must include:
- CVA variants — use
class-variance-authorityfor visual variants cn()composition — compose all className props throughcn()data-slotattribute — for component identification- Named exports — export the component and its variants
- Radix UI primitives — use Radix for accessible behavior when the component is interactive
- TypeScript types — extend the appropriate HTML element props
3. Add to registry.json
Add an entry to registry.json:
{
"name": "my-component",
"type": "registry:ui",
"description": "A brief description of what the component does.",
"dependencies": ["class-variance-authority"],
"registryDependencies": [],
"files": [
{
"path": "components/ui/my-component.tsx",
"type": "registry:ui"
}
]
}Field reference:
| Field | Required | Description |
|---|---|---|
name | Yes | Kebab-case identifier (must match the file name) |
type | Yes | registry:ui, registry:hook, or registry:lib |
description | Yes | One-line description |
dependencies | Yes | npm package dependencies (can be empty array) |
registryDependencies | Yes | Names of other registry components this depends on |
files | Yes | Array of files that make up the component |
4. Rebuild the static registry
pnpm registry:buildThis generates the static JSON files in public/r/.
5. Test the component
Verify it serves correctly via the API:
pnpm dev
curl http://localhost:3000/api/v1/ui/my-componentThe response should include the component metadata and inline source code.
6. Add tests
Add tests in __tests__/components/:
import { render, screen } from "@testing-library/react"
import { MyComponent } from "@/components/ui/my-component"
describe("MyComponent", () => {
it("renders with default variant", () => {
render(<MyComponent>Content</MyComponent>)
expect(screen.getByText("Content")).toBeInTheDocument()
})
it("applies variant classes", () => {
render(<MyComponent variant="outline">Content</MyComponent>)
const el = screen.getByText("Content")
expect(el).toHaveClass("border")
})
})7. Run the full test suite
pnpm test
pnpm lint
pnpm typecheckAll checks must pass before submitting.
Checklist
Before submitting a PR:
- Component follows CVA + Radix + cn() pattern
- Uses only CSS custom properties (no hardcoded colors)
- Has
data-slotattribute - Has named exports (not default export)
- Entry added to
registry.jsonwith correct schema -
pnpm registry:buildruns without errors - API serves the component correctly
- Tests pass (
pnpm test) - Lint passes (
pnpm lint) - Types check (
pnpm typecheck) - Accessibility: APCA AAA contrast, 48px touch targets, keyboard nav
Last updated on