Skip to content

Commit 29ee1c7

Browse files
authored
Initial commit
0 parents  commit 29ee1c7

40 files changed

+7306
-0
lines changed

.gitignore

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.*
7+
.yarn/*
8+
!.yarn/patches
9+
!.yarn/plugins
10+
!.yarn/releases
11+
!.yarn/versions
12+
13+
# testing
14+
/coverage
15+
16+
# next.js
17+
/.next/
18+
/out/
19+
20+
# production
21+
/build
22+
23+
# misc
24+
.DS_Store
25+
*.pem
26+
27+
# debug
28+
npm-debug.log*
29+
yarn-debug.log*
30+
yarn-error.log*
31+
.pnpm-debug.log*
32+
33+
# env files (can opt-in for committing if needed)
34+
.env*
35+
36+
# vercel
37+
.vercel
38+
39+
# typescript
40+
*.tsbuildinfo
41+
next-env.d.ts

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# registry-template
2+
3+
You can use the `shadcn` CLI to run your own component registry. Running your own
4+
component registry allows you to distribute your custom components, hooks, pages, and
5+
other files to any React project.
6+
7+
> [!IMPORTANT]
8+
> This template uses Tailwind v4. For Tailwind v3, see [registry-template-v3](https://github.com/shadcn-ui/registry-template-v3).
9+
10+
## Getting Started
11+
12+
This is a template for creating a custom registry using Next.js.
13+
14+
- The template uses a `registry.json` file to define components and their files.
15+
- The `shadcn build` command is used to build the registry.
16+
- The registry items are served as static files under `public/r/[name].json`.
17+
- The template also includes a route handler for serving registry items.
18+
- Every registry item are compatible with the `shadcn` CLI.
19+
- We have also added v0 integration using the `Open in v0` api.
20+
21+
## Documentation
22+
23+
Visit the [shadcn documentation](https://ui.shadcn.com/docs/registry) to view the full documentation.

app/favicon.ico

25.3 KB
Binary file not shown.

app/globals.css

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
@import "tailwindcss";
2+
@import "tw-animate-css";
3+
4+
@custom-variant dark (&:is(.dark *));
5+
6+
@theme inline {
7+
--color-background: var(--background);
8+
--color-foreground: var(--foreground);
9+
--font-sans: var(--font-geist-sans);
10+
--font-mono: var(--font-geist-mono);
11+
--color-sidebar-ring: var(--sidebar-ring);
12+
--color-sidebar-border: var(--sidebar-border);
13+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
14+
--color-sidebar-accent: var(--sidebar-accent);
15+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
16+
--color-sidebar-primary: var(--sidebar-primary);
17+
--color-sidebar-foreground: var(--sidebar-foreground);
18+
--color-sidebar: var(--sidebar);
19+
--color-chart-5: var(--chart-5);
20+
--color-chart-4: var(--chart-4);
21+
--color-chart-3: var(--chart-3);
22+
--color-chart-2: var(--chart-2);
23+
--color-chart-1: var(--chart-1);
24+
--color-ring: var(--ring);
25+
--color-input: var(--input);
26+
--color-border: var(--border);
27+
--color-destructive: var(--destructive);
28+
--color-accent-foreground: var(--accent-foreground);
29+
--color-accent: var(--accent);
30+
--color-muted-foreground: var(--muted-foreground);
31+
--color-muted: var(--muted);
32+
--color-secondary-foreground: var(--secondary-foreground);
33+
--color-secondary: var(--secondary);
34+
--color-primary-foreground: var(--primary-foreground);
35+
--color-primary: var(--primary);
36+
--color-popover-foreground: var(--popover-foreground);
37+
--color-popover: var(--popover);
38+
--color-card-foreground: var(--card-foreground);
39+
--color-card: var(--card);
40+
--radius-sm: calc(var(--radius) - 4px);
41+
--radius-md: calc(var(--radius) - 2px);
42+
--radius-lg: var(--radius);
43+
--radius-xl: calc(var(--radius) + 4px);
44+
}
45+
46+
:root {
47+
--radius: 0.625rem;
48+
--background: oklch(1 0 0);
49+
--foreground: oklch(0.145 0 0);
50+
--card: oklch(1 0 0);
51+
--card-foreground: oklch(0.145 0 0);
52+
--popover: oklch(1 0 0);
53+
--popover-foreground: oklch(0.145 0 0);
54+
--primary: oklch(0.205 0 0);
55+
--primary-foreground: oklch(0.985 0 0);
56+
--secondary: oklch(0.97 0 0);
57+
--secondary-foreground: oklch(0.205 0 0);
58+
--muted: oklch(0.97 0 0);
59+
--muted-foreground: oklch(0.556 0 0);
60+
--accent: oklch(0.97 0 0);
61+
--accent-foreground: oklch(0.205 0 0);
62+
--destructive: oklch(0.577 0.245 27.325);
63+
--border: oklch(0.922 0 0);
64+
--input: oklch(0.922 0 0);
65+
--ring: oklch(0.708 0 0);
66+
--chart-1: oklch(0.646 0.222 41.116);
67+
--chart-2: oklch(0.6 0.118 184.704);
68+
--chart-3: oklch(0.398 0.07 227.392);
69+
--chart-4: oklch(0.828 0.189 84.429);
70+
--chart-5: oklch(0.769 0.188 70.08);
71+
--sidebar: oklch(0.985 0 0);
72+
--sidebar-foreground: oklch(0.145 0 0);
73+
--sidebar-primary: oklch(0.205 0 0);
74+
--sidebar-primary-foreground: oklch(0.985 0 0);
75+
--sidebar-accent: oklch(0.97 0 0);
76+
--sidebar-accent-foreground: oklch(0.205 0 0);
77+
--sidebar-border: oklch(0.922 0 0);
78+
--sidebar-ring: oklch(0.708 0 0);
79+
}
80+
81+
.dark {
82+
--background: oklch(0.145 0 0);
83+
--foreground: oklch(0.985 0 0);
84+
--card: oklch(0.205 0 0);
85+
--card-foreground: oklch(0.985 0 0);
86+
--popover: oklch(0.205 0 0);
87+
--popover-foreground: oklch(0.985 0 0);
88+
--primary: oklch(0.922 0 0);
89+
--primary-foreground: oklch(0.205 0 0);
90+
--secondary: oklch(0.269 0 0);
91+
--secondary-foreground: oklch(0.985 0 0);
92+
--muted: oklch(0.269 0 0);
93+
--muted-foreground: oklch(0.708 0 0);
94+
--accent: oklch(0.269 0 0);
95+
--accent-foreground: oklch(0.985 0 0);
96+
--destructive: oklch(0.704 0.191 22.216);
97+
--border: oklch(1 0 0 / 10%);
98+
--input: oklch(1 0 0 / 15%);
99+
--ring: oklch(0.556 0 0);
100+
--chart-1: oklch(0.488 0.243 264.376);
101+
--chart-2: oklch(0.696 0.17 162.48);
102+
--chart-3: oklch(0.769 0.188 70.08);
103+
--chart-4: oklch(0.627 0.265 303.9);
104+
--chart-5: oklch(0.645 0.246 16.439);
105+
--sidebar: oklch(0.205 0 0);
106+
--sidebar-foreground: oklch(0.985 0 0);
107+
--sidebar-primary: oklch(0.488 0.243 264.376);
108+
--sidebar-primary-foreground: oklch(0.985 0 0);
109+
--sidebar-accent: oklch(0.269 0 0);
110+
--sidebar-accent-foreground: oklch(0.985 0 0);
111+
--sidebar-border: oklch(1 0 0 / 10%);
112+
--sidebar-ring: oklch(0.556 0 0);
113+
}
114+
115+
@layer base {
116+
* {
117+
@apply border-border outline-ring/50;
118+
}
119+
body {
120+
@apply bg-background text-foreground;
121+
}
122+
}

app/layout.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type { Metadata } from "next";
2+
import { Geist, Geist_Mono } from "next/font/google";
3+
import "./globals.css";
4+
5+
const geistSans = Geist({
6+
variable: "--font-geist-sans",
7+
subsets: ["latin"],
8+
});
9+
10+
const geistMono = Geist_Mono({
11+
variable: "--font-geist-mono",
12+
subsets: ["latin"],
13+
});
14+
15+
export const metadata: Metadata = {
16+
title: "Create Next App",
17+
description: "Generated by create next app",
18+
};
19+
20+
export default function RootLayout({
21+
children,
22+
}: Readonly<{
23+
children: React.ReactNode;
24+
}>) {
25+
return (
26+
<html lang="en">
27+
<body
28+
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
29+
>
30+
{children}
31+
</body>
32+
</html>
33+
);
34+
}

app/page.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import * as React from "react"
2+
import { OpenInV0Button } from "@/components/open-in-v0-button"
3+
import { HelloWorld } from "@/registry/new-york/blocks/hello-world/hello-world"
4+
import { ExampleForm } from "@/registry/new-york/blocks/example-form/example-form"
5+
import PokemonPage from "@/registry/new-york/blocks/complex-component/page"
6+
import { ExampleCard } from "@/registry/new-york/blocks/example-with-css/example-card"
7+
// This page displays items from the custom registry.
8+
// You are free to implement this with your own design as needed.
9+
10+
export default function Home() {
11+
return (
12+
<div className="max-w-3xl mx-auto flex flex-col min-h-svh px-4 py-8 gap-8">
13+
<header className="flex flex-col gap-1">
14+
<h1 className="text-3xl font-bold tracking-tight">Custom Registry</h1>
15+
<p className="text-muted-foreground">
16+
A custom registry for distributing code using shadcn.
17+
</p>
18+
</header>
19+
<main className="flex flex-col flex-1 gap-8">
20+
<div className="flex flex-col gap-4 border rounded-lg p-4 min-h-[450px] relative">
21+
<div className="flex items-center justify-between">
22+
<h2 className="text-sm text-muted-foreground sm:pl-3">
23+
A simple hello world component
24+
</h2>
25+
<OpenInV0Button name="hello-world" className="w-fit" />
26+
</div>
27+
<div className="flex items-center justify-center min-h-[400px] relative">
28+
<HelloWorld />
29+
</div>
30+
</div>
31+
32+
<div className="flex flex-col gap-4 border rounded-lg p-4 min-h-[450px] relative">
33+
<div className="flex items-center justify-between">
34+
<h2 className="text-sm text-muted-foreground sm:pl-3">
35+
A contact form with Zod validation.
36+
</h2>
37+
<OpenInV0Button name="example-form" className="w-fit" />
38+
</div>
39+
<div className="flex items-center justify-center min-h-[500px] relative">
40+
<ExampleForm />
41+
</div>
42+
</div>
43+
44+
<div className="flex flex-col gap-4 border rounded-lg p-4 min-h-[450px] relative">
45+
<div className="flex items-center justify-between">
46+
<h2 className="text-sm text-muted-foreground sm:pl-3">
47+
A complex component showing hooks, libs and components.
48+
</h2>
49+
<OpenInV0Button name="complex-component" className="w-fit" />
50+
</div>
51+
<div className="flex items-center justify-center min-h-[400px] relative">
52+
<PokemonPage />
53+
</div>
54+
</div>
55+
56+
<div className="flex flex-col gap-4 border rounded-lg p-4 min-h-[450px] relative">
57+
<div className="flex items-center justify-between">
58+
<h2 className="text-sm text-muted-foreground sm:pl-3">
59+
A login form with a CSS file.
60+
</h2>
61+
<OpenInV0Button name="example-with-css" className="w-fit" />
62+
</div>
63+
<div className="flex items-center justify-center min-h-[400px] relative">
64+
<ExampleCard />
65+
</div>
66+
</div>
67+
</main>
68+
</div>
69+
)
70+
}

components.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"$schema": "https://ui.shadcn.com/schema.json",
3+
"style": "new-york",
4+
"rsc": true,
5+
"tsx": true,
6+
"tailwind": {
7+
"config": "",
8+
"css": "app/globals.css",
9+
"baseColor": "neutral",
10+
"cssVariables": true,
11+
"prefix": ""
12+
},
13+
"aliases": {
14+
"components": "@/components",
15+
"utils": "@/lib/utils",
16+
"ui": "@/components/ui",
17+
"lib": "@/lib",
18+
"hooks": "@/hooks"
19+
},
20+
"iconLibrary": "lucide"
21+
}

components/open-in-v0-button.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { Button } from "@/registry/new-york/ui/button"
2+
import { cn } from "@/lib/utils"
3+
4+
export function OpenInV0Button({
5+
name,
6+
className,
7+
}: { name: string } & React.ComponentProps<typeof Button>) {
8+
return (
9+
<Button
10+
aria-label="Open in v0"
11+
size="sm"
12+
className={cn(
13+
"shadow-none bg-black text-white hover:bg-black hover:text-white dark:bg-white dark:text-black",
14+
className
15+
)}
16+
asChild
17+
>
18+
<a
19+
href={`https://v0.dev/chat/api/open?url=${process.env.NEXT_PUBLIC_BASE_URL}/r/${name}.json`}
20+
target="_blank"
21+
rel="noreferrer"
22+
>
23+
Open in{" "}
24+
<svg
25+
viewBox="0 0 40 20"
26+
fill="none"
27+
xmlns="http://www.w3.org/2000/svg"
28+
className="h-5 w-5 text-current"
29+
>
30+
<path
31+
d="M23.3919 0H32.9188C36.7819 0 39.9136 3.13165 39.9136 6.99475V16.0805H36.0006V6.99475C36.0006 6.90167 35.9969 6.80925 35.9898 6.71766L26.4628 16.079C26.4949 16.08 26.5272 16.0805 26.5595 16.0805H36.0006V19.7762H26.5595C22.6964 19.7762 19.4788 16.6139 19.4788 12.7508V3.68923H23.3919V12.7508C23.3919 12.9253 23.4054 13.0977 23.4316 13.2668L33.1682 3.6995C33.0861 3.6927 33.003 3.68923 32.9188 3.68923H23.3919V0Z"
32+
fill="currentColor"
33+
></path>
34+
<path
35+
d="M13.7688 19.0956L0 3.68759H5.53933L13.6231 12.7337V3.68759H17.7535V17.5746C17.7535 19.6705 15.1654 20.6584 13.7688 19.0956Z"
36+
fill="currentColor"
37+
></path>
38+
</svg>
39+
</a>
40+
</Button>
41+
)
42+
}

eslint.config.mjs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { dirname } from "path";
2+
import { fileURLToPath } from "url";
3+
import { FlatCompat } from "@eslint/eslintrc";
4+
5+
const __filename = fileURLToPath(import.meta.url);
6+
const __dirname = dirname(__filename);
7+
8+
const compat = new FlatCompat({
9+
baseDirectory: __dirname,
10+
});
11+
12+
const eslintConfig = [
13+
...compat.extends("next/core-web-vitals", "next/typescript"),
14+
];
15+
16+
export default eslintConfig;

lib/utils.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { clsx, type ClassValue } from "clsx"
2+
import { twMerge } from "tailwind-merge"
3+
4+
export function cn(...inputs: ClassValue[]) {
5+
return twMerge(clsx(inputs))
6+
}

0 commit comments

Comments
 (0)