Skip to Content
RegistryContributing

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-authority for visual variants
  • cn() composition — compose all className props through cn()
  • data-slot attribute — 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:

FieldRequiredDescription
nameYesKebab-case identifier (must match the file name)
typeYesregistry:ui, registry:hook, or registry:lib
descriptionYesOne-line description
dependenciesYesnpm package dependencies (can be empty array)
registryDependenciesYesNames of other registry components this depends on
filesYesArray of files that make up the component

4. Rebuild the static registry

pnpm registry:build

This 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-component

The 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 typecheck

All 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-slot attribute
  • Has named exports (not default export)
  • Entry added to registry.json with correct schema
  • pnpm registry:build runs 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