Registry Setup
How to set up your own component registry like ComponentVault.
Registry Setup
Learn how to create your own shadcn-compatible component registry.
Overview
A shadcn registry is a JSON API that provides component metadata and source code. The CLI fetches this JSON and installs the component in the user's project.
Registry Schema
Each component in the registry follows this schema:
{
"name": "animated-card",
"type": "registry:ui",
"description": "A card with smooth hover animations.",
"dependencies": ["framer-motion"],
"files": [
{
"path": "ui/animated-card.tsx",
"content": "// Component source code...",
"type": "registry:ui"
}
]
}Schema Fields
| Field | Type | Description |
|---|---|---|
name | string | Component identifier (kebab-case) |
type | string | Always "registry:ui" for UI components |
description | string | Brief component description |
dependencies | string[] | NPM packages required |
files | object[] | Array of file objects |
File Object
| Field | Type | Description |
|---|---|---|
path | string | Relative path where file will be created |
content | string | Full source code of the file |
type | string | File type (registry:ui, registry:lib, etc.) |
API Endpoint
Create an API endpoint that returns the component JSON:
// app/r/[name]/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ name: string }> }
) {
const { name } = await params;
const slug = name.replace(/\.json$/, '');
const component = getComponent(slug);
if (!component) {
return NextResponse.json(
{ error: 'Not found' },
{ status: 404 }
);
}
return NextResponse.json(component);
}// pages/api/r/[name].ts
import type { NextApiRequest, NextApiResponse } from 'next';
export default function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { name } = req.query;
const slug = String(name).replace(/\.json$/, '');
const component = getComponent(slug);
if (!component) {
return res.status(404).json({ error: 'Not found' });
}
return res.json(component);
}// server.js
app.get('/r/:name', (req, res) => {
const slug = req.params.name.replace(/\.json$/, '');
const component = getComponent(slug);
if (!component) {
return res.status(404).json({ error: 'Not found' });
}
return res.json(component);
});Authentication
For premium components, validate the license key:
export async function GET(request: NextRequest, { params }) {
const { name } = await params;
const component = getComponent(name);
if (component.isPremium) {
const authHeader = request.headers.get('authorization');
const token = authHeader?.replace('Bearer ', '');
if (!isValidLicense(token)) {
return NextResponse.json(
{ error: 'Valid license required' },
{ status: 401 }
);
}
}
return NextResponse.json(component);
}Static Registry
Alternatively, host static JSON files:
public/
r/
animated-card.json
search-input.json
status-badge.jsonThis works for free components but doesn't support authentication.
Testing Your Registry
Test with curl:
curl https://your-registry.com/r/animated-card.jsonOr install directly:
npx shadcn@latest add https://your-registry.com/animated-cardThe CLI automatically appends .json to the URL if not present.
Best Practices
- Version your components - Include version in metadata
- Validate thoroughly - Test components before publishing
- Document dependencies - List all required packages
- Use TypeScript - Export proper types for all components
- Follow conventions - Match shadcn naming and structure