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/utilsfor conditional/merged class names - Semantic colors: Use design tokens (
bg-primary,text-muted-foreground), never raw values likebg-blue-500 - Spacing: Use
flexwithgap-*, neverspace-x-*orspace-y-* - Variants: Use
class-variance-authority(cva) when the component has multiple visual options - Hooks: Import directly —
import { useState } from 'react', neverReact.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
transitionorvariantsprops (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>.tsxFollow 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.mdxThe page must follow this exact order:
metadataexport —title,description, andalternates.canonicalset to/docs/components/<name><JsonLd />— breadcrumb structured data (Home → Docs → Component)# Title— component name as heading- Short description — one paragraph
## Installation—<InstallSnippet registryUrl="https://prismaui.com/components/<name>.json" />## Import— single code block with the import statement---separator## Usage/ variant sections — each with a<ComponentPreview>above its code block## 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
useStateor interactivity, extract it into a_demo-<variant>.tsxclient 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>.jsonThe <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:
- Edit the component file at
src/components/ui/<component-name>.tsx. - Update the documentation if any props, variants, or behavior changed.
- Regenerate the registry entry using the Component Generator at
/component-generatorand replace the file atpublic/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 type | Bump | Example |
|---|---|---|
| New feature, new variant, new prop | minor | 1.0.0 → 1.1.0 |
| Bug fix, style correction | patch | 1.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-500etc.) -
gap-*used for spacing (nospace-x-*orspace-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-generatorand placed atpublic/components/<name>.json -
<InstallSnippet>present in the documentation page - Component slug added to
src/app/sitemap.ts - Component row added to
README.md -
package.jsonversion bumped (minor)
Component update
- Component file updated
- Documentation updated if API changed
- Registry JSON regenerated via
/component-generatorand replaced atpublic/components/<name>.json - Component version bumped in registry JSON (minor for new feature, patch for bug fix)