niels segers
cover

Publishing custom shadcn/ui components

Sep 6, 2024

With the August 2024 update of the shadcn/ui CLI tool, developers can now add support for custom components. This opens up exciting possibilities for creating and sharing your own component libraries. In this post, we'll walk through the process of setting up your own shadcn/ui registry.

1. Set Up Your Project Structure

First, let's create a directory structure for our custom registry:

my-registry/
├── src/
│   └── components/
│       └── ui/
│           └── component.tsx
├── scripts/
│   └── build.ts
└── public/

The easiest way to get started is to start from something like a fresh Next.js app that you can easily deploy to Vercel afterwards.

2. Create Your Custom Component

Let's say we want to add a custom component to our registry. Create a file src/components/ui/component.tsx and write your component code.

3. Create the Registry Build Script

Now, let's create a script to generate the registry JSON files. Create a file scripts/build.ts and configure your components:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 import fs from "fs"; import path from "path"; export interface Schema { name: string; type: "registry:ui"; registryDependencies: string[]; dependencies: string[]; devDependencies: string[]; tailwind: { config?: Record<string, object>; }; cssVars: { light: Record<string, string>; dark: Record<string, string>; }; files: Array<{ path: string; content: string; type: "registry:ui"; }>; } type ComponentDefinition = Partial< Pick< Schema, | "dependencies" | "devDependencies" | "registryDependencies" | "cssVars" | "tailwind" > > & { name: string; path: string; }; // Define the components and their dependencies that should be registered const components: ComponentDefinition[] = [ { name: "component", path: path.join(__dirname, "../src/components/ui/component.tsx"), registryDependencies: ["button", "input"], dependencies: ["lucide-react"], cssVars: { light: {}, dark: {}, }, tailwind: { config: { theme: { extend: { colors: {}, }, }, }, }, }, ]; // Create the registry directory if it doesn't exist const registry = path.join(__dirname, "../public"); if (!fs.existsSync(registry)) { fs.mkdirSync(registry); } // Create the registry files for (const component of components) { const content = fs.readFileSync(component.path, "utf8"); const schema = { name: component.name, type: "registry:ui", registryDependencies: component.registryDependencies || [], dependencies: component.dependencies || [], devDependencies: component.devDependencies || [], tailwind: component.tailwind || {}, cssVars: component.cssVars || { light: {}, dark: {}, }, files: [ { path: `${component.name}.tsx`, content, type: "registry:ui", }, ], } satisfies Schema; fs.writeFileSync( path.join(registry, `${component.name}.json`), JSON.stringify(schema, null, 2) ); }

This script reads your component files, creates a schema for each component, and generates JSON files in the public directory.

4. Add a Build Script

Add a script to your package.json to run the registry generation:

1 2 3 "scripts": { "build:registry": "npx bun ./scripts/build.ts" }

5. Generate the Registry

Run the build script to generate your registry:

1 npm run build:registry

This will create a JSON file for each component in the public directory.

6. Use Your Custom Registry

First ensure that everything is working as expected by testing it out locally on a personal project where shadcn is already set up.

1 2 $ npm run dev $ npx shadcn add "http://localhost:3000/component.json"

This will fetch the component.json schema from your registry and add the component to your project.

Conclusion

Creating your own shadcn registry enables you to share custom components across projects or with the community. With these steps, you can establish a registry for your component library, making it simple to reuse and distribute your UI components.