Skip to Content
Prisma UI - React component library
ComponentsContributing

Contributing a Component

This guide walks through the full process of adding a new component to Prisma UI, from writing the component file to publishing its documentation and registry entry. It also covers what to do when you are only updating an existing component.

Using AI? There is a skill in .agents/skills/add-component/ that guides an AI agent through this entire workflow automatically. Load it with any agent that supports skills.


Component conventions

Follow these rules in every component:

  • Class merging: Always use cn() from @/lib/utils for conditional/merged class names
  • Semantic colors: Use design tokens (bg-primary, text-muted-foreground), never raw values like bg-blue-500
  • Spacing: Use flex with gap-*, never space-x-* or space-y-*
  • Variants: Use class-variance-authority (cva) when the component has multiple visual options
  • Hooks: Import directly — import { useState } from 'react', never React.useState
  • Return types: Component functions always return React.ReactNode
  • Client directive: Add "use client" if the component uses hooks, event handlers, or browser APIs
  • Animations: Keep keyframe animations in src/components/ui/style.css, not inline or in separate CSS files
  • Default animation props: When a component accepts transition or variants props (Framer Motion), provide sensible defaults so it works out of the box — users can override them, but should never be forced to pass them

Adding a new component

Follow these steps in order. Each step builds on the previous one.

1. Check shadcn availability

Before writing any new UI component, verify if shadcn/ui already provides it:

npx shadcn@latest add <component-name>
  • If shadcn has the component → install it and use it as-is, or wrap it (see below)
  • If shadcn has something similar → install it and extend via wrapper
  • If nothing exists → create from scratch following the conventions above

When extending a shadcn component, create a wrapper file instead of editing the base. For example, card-hover-effect.tsx instead of modifying card.tsx.

2. Create the component file

Add the component source at:

src/components/ui/<component-name>.tsx

Follow the component conventions listed above and use existing components as reference.

If the component requires shared animation keyframes or [data-variant] CSS properties, you do not need to modify style.css yourself — it is already included as a registry dependency for components that need it.

3. Create the documentation page

Create a new directory and page at:

src/app/docs/components/<component-name>/page.mdx

The page must follow this exact order:

  1. metadata exporttitle, description, and alternates.canonical set to /docs/components/<name>
  2. <JsonLd /> — breadcrumb structured data (Home → Docs → Component)
  3. # Title — component name as heading
  4. Short description — one paragraph
  5. ## Installation<InstallSnippet registryUrl="https://prismaui.com/components/<name>.json" />
  6. ## Import — single code block with the import statement
  7. --- separator
  8. ## Usage / variant sections — each with a <ComponentPreview> above its code block
  9. ## Props — props table at the very end

ComponentPreview rules

  • Every non-import code block must have a <ComponentPreview> above it showing the rendered result
  • Preview blocks should contain only the component — no extra explanation, no wrapping layout
  • When a component needs images, use Lorem Picsum  with next/image. Vary dimensions slightly between items for different images
  • If a demo requires useState or interactivity, extract it into a _demo-<variant>.tsx client component next to the doc page and import it
  • When icons or third-party imports are needed, split them into a separate code block before the usage block

5. Register the component in the docs sidebar

Open src/app/docs/components/_meta.ts and add an entry for the new component:

'<component-name>': { title: '<Display Name>', },

Place it under the appropriate separator (-- general or -- cards), or add a new separator if the component belongs to a new category.

6. Add the registry entry

Use the Component Generator at /component-generator to fill in the component details and generate its registry JSON. This is the standard and official method for creating registry entries.

Download the generated JSON and place it at:

public/components/<component-name>.json

The <InstallSnippet> on the documentation page will become valid once this file is in place, since it is served directly from /public/components/.

7. Add the install snippet to the documentation

If the <InstallSnippet> block is not already present in the documentation page, add it to the ## Installation section:

<InstallSnippet registryUrl='https://prismaui.com/components/<component-name>.json' />

This is typically added in step 2, but verify it is present and points to the correct URL after the registry JSON is generated.

8. Add to sitemap

Open src/app/sitemap.ts and add the component slug to the components array:

const components = [ // ... existing entries '<component-name>', ];

9. Add the component to the README

Open README.md and add a row to the Components table:

| **<Display Name>** | <One-line description of what the component does.> |

Keep the description concise — one sentence, no trailing period is fine.

10. Bump the package version

Open package.json and increment the version field with a minor bump to record that a new component was added:

{ "version": "1.1.0" }

A minor bump signals a backward-compatible addition to the library.


Updating an existing component

When you are only changing an existing component — fixing a bug, adding a variant, adjusting styles — the scope is smaller:

  1. Edit the component file at src/components/ui/<component-name>.tsx.
  2. Update the documentation if any props, variants, or behavior changed.
  3. Regenerate the registry entry using the Component Generator at /component-generator and replace the file at public/components/<component-name>.json.

Component versioning

When updating a component, bump the version field inside the component’s registry JSON (not the root package.json) according to the nature of the change:

Change typeBumpExample
New feature, new variant, new propminor1.0.0 → 1.1.0
Bug fix, style correctionpatch1.0.0 → 1.0.1

Do not bump the root package.json version for a component update — that is reserved for new additions.


Checklist

Use this as a quick reference before opening a pull request.

New component

  • Checked shadcn registry first (npx shadcn@latest add <name>)
  • If shadcn had it: installed via CLI, created wrapper instead of modifying base
  • Component file created at src/components/ui/<name>.tsx
  • "use client" present if component uses hooks, event handlers, or browser APIs
  • cn() used for all class merging
  • Semantic color tokens used (no raw bg-blue-500 etc.)
  • gap-* used for spacing (no space-x-* or space-y-*)
  • Hooks imported directly (import { useState } from 'react')
  • Return type is React.ReactNode
  • Documentation page follows correct order (metadata → title → Installation → Import → --- → sections → Props)
  • Every non-import code block has a <ComponentPreview> above it
  • Entry added to src/app/docs/components/_meta.ts
  • Registry JSON generated via /component-generator and placed at public/components/<name>.json
  • <InstallSnippet> present in the documentation page
  • Component slug added to src/app/sitemap.ts
  • Component row added to README.md
  • package.json version bumped (minor)

Component update

  • Component file updated
  • Documentation updated if API changed
  • Registry JSON regenerated via /component-generator and replaced at public/components/<name>.json
  • Component version bumped in registry JSON (minor for new feature, patch for bug fix)
Last updated on