ComponentVault

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

FieldTypeDescription
namestringComponent identifier (kebab-case)
typestringAlways "registry:ui" for UI components
descriptionstringBrief component description
dependenciesstring[]NPM packages required
filesobject[]Array of file objects

File Object

FieldTypeDescription
pathstringRelative path where file will be created
contentstringFull source code of the file
typestringFile 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.json

This works for free components but doesn't support authentication.

Testing Your Registry

Test with curl:

curl https://your-registry.com/r/animated-card.json

Or install directly:

npx shadcn@latest add https://your-registry.com/animated-card

The CLI automatically appends .json to the URL if not present.

Best Practices

  1. Version your components - Include version in metadata
  2. Validate thoroughly - Test components before publishing
  3. Document dependencies - List all required packages
  4. Use TypeScript - Export proper types for all components
  5. Follow conventions - Match shadcn naming and structure

On this page