mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-14 11:21:36 +00:00
Compare commits
96 Commits
shadcn@2.6
...
shadcn/doc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21686d7e83 | ||
|
|
bc56bb6022 | ||
|
|
d44ab7ffa4 | ||
|
|
8c143c5de2 | ||
|
|
a4ba91ab44 | ||
|
|
f854190b53 | ||
|
|
200d3aba60 | ||
|
|
fd44aead8e | ||
|
|
cc564974c6 | ||
|
|
f72d0cf509 | ||
|
|
396275e46a | ||
|
|
296feb28a2 | ||
|
|
a941287411 | ||
|
|
2e34c95c4e | ||
|
|
fed7e3bfdc | ||
|
|
4f5333ea7a | ||
|
|
b5b8deedde | ||
|
|
7d71b02fb1 | ||
|
|
b3639227d0 | ||
|
|
a4a3600757 | ||
|
|
a426fea941 | ||
|
|
6e870c3993 | ||
|
|
68aa3389de | ||
|
|
2e9ccede8f | ||
|
|
fc8927a1f9 | ||
|
|
ccfd14946b | ||
|
|
01c02b289a | ||
|
|
a80ab37483 | ||
|
|
469250115f | ||
|
|
2c164b0f22 | ||
|
|
578f83cbef | ||
|
|
07eda36b13 | ||
|
|
0eccdc9c5f | ||
|
|
0940c6aec7 | ||
|
|
e244952500 | ||
|
|
0e3d6b24d3 | ||
|
|
cef5af9ed3 | ||
|
|
6deb0fdbb6 | ||
|
|
e9ae79f874 | ||
|
|
d891132f2a | ||
|
|
873f7f2773 | ||
|
|
e6778dee87 | ||
|
|
97a8de1c1b | ||
|
|
19d7fbb731 | ||
|
|
a9ab05ad83 | ||
|
|
6ac114ae68 | ||
|
|
d5770e4350 | ||
|
|
4730276256 | ||
|
|
4e04567b07 | ||
|
|
6f63b04d28 | ||
|
|
e38228b574 | ||
|
|
8807103586 | ||
|
|
3424ab709e | ||
|
|
4a86a55cac | ||
|
|
2926574d0e | ||
|
|
20e913d8e1 | ||
|
|
3433aaffaa | ||
|
|
d9cdc3f7ae | ||
|
|
e75e7b3866 | ||
|
|
ed5237c231 | ||
|
|
f85ca066dc | ||
|
|
54e66d4450 | ||
|
|
6c341c16ae | ||
|
|
06d03d64f4 | ||
|
|
6407a3b330 | ||
|
|
96b15f6090 | ||
|
|
2fe9cf6d26 | ||
|
|
728cb4cfa5 | ||
|
|
db93787712 | ||
|
|
1cdd6c1645 | ||
|
|
4983c6e1f4 | ||
|
|
7443edcfb0 | ||
|
|
9d9a33be52 | ||
|
|
d544a7f7a5 | ||
|
|
48fe0d709f | ||
|
|
ed244ea0b5 | ||
|
|
b8fede1742 | ||
|
|
84d6c83bad | ||
|
|
5b8ee41511 | ||
|
|
7c3d34cdc9 | ||
|
|
56c4c83511 | ||
|
|
2821cb0e39 | ||
|
|
3c87402de2 | ||
|
|
20a88e1f15 | ||
|
|
cb19ab8464 | ||
|
|
cf1851ca09 | ||
|
|
c86c27a2ff | ||
|
|
8847126c65 | ||
|
|
65350857a4 | ||
|
|
40c7473c7e | ||
|
|
4698ee960f | ||
|
|
2ae0e5a07b | ||
|
|
431af4f7ff | ||
|
|
c1357982e8 | ||
|
|
92cfb9a30e | ||
|
|
c5d90c718a |
5
.changeset/bumpy-gifts-relate.md
Normal file
5
.changeset/bumpy-gifts-relate.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"shadcn": major
|
||||
---
|
||||
|
||||
bump all dependencies
|
||||
5
.changeset/chatty-windows-stop.md
Normal file
5
.changeset/chatty-windows-stop.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"shadcn": major
|
||||
---
|
||||
|
||||
add new mcp server and command
|
||||
@@ -7,5 +7,5 @@
|
||||
"access": "public",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": ["www", "v4"]
|
||||
"ignore": ["www", "v4", "tests"]
|
||||
}
|
||||
|
||||
5
.changeset/dirty-teachers-raise.md
Normal file
5
.changeset/dirty-teachers-raise.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"shadcn": major
|
||||
---
|
||||
|
||||
add view and search commands
|
||||
5
.changeset/good-drinks-repeat.md
Normal file
5
.changeset/good-drinks-repeat.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"shadcn": major
|
||||
---
|
||||
|
||||
update getRegistry, getRegistryItems and resolveRegistryItems apis
|
||||
5
.changeset/gorgeous-lemons-try.md
Normal file
5
.changeset/gorgeous-lemons-try.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"shadcn": major
|
||||
---
|
||||
|
||||
deprecate fetchRegistry and resolveRegistryTree
|
||||
5
.changeset/hungry-melons-press.md
Normal file
5
.changeset/hungry-melons-press.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"shadcn": minor
|
||||
---
|
||||
|
||||
update file handling for monorepo
|
||||
5
.changeset/lazy-readers-invent.md
Normal file
5
.changeset/lazy-readers-invent.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"shadcn": major
|
||||
---
|
||||
|
||||
copy registry.json on build
|
||||
5
.changeset/little-turtles-taste.md
Normal file
5
.changeset/little-turtles-taste.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"shadcn": major
|
||||
---
|
||||
|
||||
move schema to shadcn/schema
|
||||
5
.changeset/loud-cooks-roll.md
Normal file
5
.changeset/loud-cooks-roll.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"shadcn": major
|
||||
---
|
||||
|
||||
update signatures for getRegistry, getRegistryItems, resolveRegistryItems and searchRegistries
|
||||
5
.changeset/poor-toys-visit.md
Normal file
5
.changeset/poor-toys-visit.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"shadcn": major
|
||||
---
|
||||
|
||||
add getRegistry
|
||||
5
.changeset/rare-sloths-run.md
Normal file
5
.changeset/rare-sloths-run.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"shadcn": major
|
||||
---
|
||||
|
||||
update registry dependencies resolution algorithm
|
||||
5
.changeset/shaggy-terms-doubt.md
Normal file
5
.changeset/shaggy-terms-doubt.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"shadcn": patch
|
||||
---
|
||||
|
||||
fix monorepo init on nix system
|
||||
5
.changeset/shiny-moles-pull.md
Normal file
5
.changeset/shiny-moles-pull.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"shadcn": major
|
||||
---
|
||||
|
||||
add mcp init command
|
||||
5
.changeset/three-apes-smell.md
Normal file
5
.changeset/three-apes-smell.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"shadcn": major
|
||||
---
|
||||
|
||||
add support for namespaced registries
|
||||
9
.claude/settings.local.json
Normal file
9
.claude/settings.local.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(npm test:*)",
|
||||
"Bash(npm run typecheck:*)"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
}
|
||||
20
.github/workflows/issue-stale.yml
vendored
20
.github/workflows/issue-stale.yml
vendored
@@ -18,15 +18,15 @@ jobs:
|
||||
repo-token: ${{ secrets.STALE_TOKEN }}
|
||||
ascending: true
|
||||
days-before-issue-close: 7
|
||||
days-before-issue-stale: 365 # ~2 years
|
||||
days-before-issue-stale: 365
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
remove-issue-stale-when-updated: true
|
||||
stale-issue-label: "stale?"
|
||||
exempt-issue-labels: "roadmap,next,bug"
|
||||
stale-issue-message: "This issue has been automatically marked as stale due to one year of inactivity. It will be closed in 7 days unless there’s further input. If you believe this issue is still relevant, please leave a comment or provide updated details. Thank you."
|
||||
close-issue-message: "This issue has been automatically closed due to one year of inactivity. If you’re still experiencing a similar problem or have additional details to share, please open a new issue following our current issue template. Your updated report helps us investigate and address concerns more efficiently. Thank you for your understanding!"
|
||||
operations-per-run: 300 # 1 operation per 100 issues, the rest is to label/comment/close
|
||||
exempt-issue-labels: "roadmap,next"
|
||||
stale-issue-message: "This issue has been automatically marked as stale due to one year of inactivity. It will be closed in 7 days unless there’s further input. If you believe this issue is still relevant, please leave a comment or provide updated details. Thank you. (This is an automated message)"
|
||||
close-issue-message: "This issue has been automatically closed due to one year of inactivity. If you’re still experiencing a similar problem or have additional details to share, please open a new issue following our current issue template. Your updated report helps us investigate and address concerns more efficiently. Thank you for your understanding! (This is an automated message)"
|
||||
operations-per-run: 300
|
||||
- uses: actions/stale@v9
|
||||
id: pr-state
|
||||
name: "Mark stale PRs, close stale PRs"
|
||||
@@ -36,10 +36,10 @@ jobs:
|
||||
days-before-issue-close: -1
|
||||
days-before-issue-stale: -1
|
||||
days-before-pr-close: 7
|
||||
days-before-pr-stale: 365 # PRs with no activity in over 90 days will be marked as stale
|
||||
days-before-pr-stale: 365
|
||||
remove-pr-stale-when-updated: true
|
||||
exempt-pr-labels: "roadmap,nex,awaiting-approval,work-in-progress"
|
||||
exempt-pr-labels: "roadmap,next,bug"
|
||||
stale-pr-label: "stale?"
|
||||
stale-pr-message: "This PR has been automatically marked as stale due to one year of inactivity. It will be closed in 7 days unless there’s further input. If you believe this PR is still relevant, please leave a comment or provide updated details. Thank you."
|
||||
close-pr-message: "This PR has been automatically closed due to one year of inactivity. Thank you for your understanding!"
|
||||
operations-per-run: 300 # 1 operation per 100 issues, the rest is to label/comment/close
|
||||
stale-pr-message: "This PR has been automatically marked as stale due to one year of inactivity. It will be closed in 7 days unless there’s further input. If you believe this PR is still relevant, please leave a comment or provide updated details. Thank you. (This is an automated message)"
|
||||
close-pr-message: "This PR has been automatically closed due to one year of inactivity. Thank you for your understanding! (This is an automated message)"
|
||||
operations-per-run: 300
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
|
||||
- name: Create Version PR or Publish to NPM
|
||||
id: changesets
|
||||
uses: changesets/action@v1.4.1
|
||||
uses: changesets/action@v1
|
||||
with:
|
||||
commit: "chore(release): version packages"
|
||||
title: "chore(release): version packages"
|
||||
|
||||
6
.github/workflows/test.yml
vendored
6
.github/workflows/test.yml
vendored
@@ -8,6 +8,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: pnpm test
|
||||
env:
|
||||
NEXT_PUBLIC_APP_URL: http://localhost:4000
|
||||
NEXT_PUBLIC_V0_URL: https://v0.dev
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -39,4 +42,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Build packages
|
||||
run: pnpm build --filter=shadcn
|
||||
|
||||
- run: pnpm test
|
||||
|
||||
@@ -98,7 +98,7 @@ To run the CLI locally, you can follow the workflow:
|
||||
1. Start by running the registry (main site) to make sure the components are up to date:
|
||||
|
||||
```bash
|
||||
pnpm www:dev
|
||||
pnpm v4:dev
|
||||
```
|
||||
|
||||
2. Run the development script for the CLI:
|
||||
|
||||
2
apps/v4/.env.example
Normal file
2
apps/v4/.env.example
Normal file
@@ -0,0 +1,2 @@
|
||||
NEXT_PUBLIC_V0_URL=https://v0.dev
|
||||
NEXT_PUBLIC_APP_URL=http://localhost:4000
|
||||
1
apps/v4/.gitignore
vendored
1
apps/v4/.gitignore
vendored
@@ -32,6 +32,7 @@ yarn-error.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
!.env.example
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
@@ -15,9 +15,9 @@ import { PageNav } from "@/components/page-nav"
|
||||
import { ThemeSelector } from "@/components/theme-selector"
|
||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
||||
|
||||
const title = "Build your Component Library"
|
||||
const title = "The Foundation for your Design System"
|
||||
const description =
|
||||
"A set of beautifully-designed, accessible components and a code distribution platform. Works with your favorite frameworks. Open Source. Open Code."
|
||||
"A set of beautifully designed components that you can customize, extend, and build on. Start here then make it your own. Open Source. Open Code."
|
||||
|
||||
export const dynamic = "force-static"
|
||||
export const revalidate = false
|
||||
@@ -51,14 +51,14 @@ export default function IndexPage() {
|
||||
<div className="flex flex-1 flex-col">
|
||||
<PageHeader>
|
||||
<Announcement />
|
||||
<PageHeaderHeading>{title}</PageHeaderHeading>
|
||||
<PageHeaderHeading className="max-w-4xl">{title}</PageHeaderHeading>
|
||||
<PageHeaderDescription>{description}</PageHeaderDescription>
|
||||
<PageActions>
|
||||
<Button asChild size="sm">
|
||||
<Link href="/docs/installation">Get Started</Link>
|
||||
</Button>
|
||||
<Button asChild size="sm" variant="ghost">
|
||||
<Link href="/blocks">Browse Blocks</Link>
|
||||
<Link href="/docs/components">View Components</Link>
|
||||
</Button>
|
||||
</PageActions>
|
||||
</PageHeader>
|
||||
|
||||
@@ -10,6 +10,7 @@ import { findNeighbour } from "fumadocs-core/server"
|
||||
|
||||
import { source } from "@/lib/source"
|
||||
import { absoluteUrl } from "@/lib/utils"
|
||||
import { DocsCopyPage } from "@/components/docs-copy-page"
|
||||
import { DocsTableOfContents } from "@/components/docs-toc"
|
||||
import { OpenInV0Cta } from "@/components/open-in-v0-cta"
|
||||
import { Badge } from "@/registry/new-york-v4/ui/badge"
|
||||
@@ -102,12 +103,17 @@ export default async function Page(props: {
|
||||
<h1 className="scroll-m-20 text-4xl font-semibold tracking-tight sm:text-3xl xl:text-4xl">
|
||||
{doc.title}
|
||||
</h1>
|
||||
<div className="flex items-center gap-2 pt-1.5">
|
||||
<div className="docs-nav bg-background/80 border-border/50 fixed inset-x-0 bottom-0 isolate z-50 flex items-center gap-2 border-t px-6 py-4 backdrop-blur-sm sm:static sm:z-0 sm:border-t-0 sm:bg-transparent sm:px-0 sm:pt-1.5 sm:backdrop-blur-none">
|
||||
<DocsCopyPage
|
||||
// @ts-expect-error - revisit fumadocs types.
|
||||
page={doc.content}
|
||||
url={absoluteUrl(page.url)}
|
||||
/>
|
||||
{neighbours.previous && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="icon"
|
||||
className="extend-touch-target size-8 shadow-none md:size-7"
|
||||
className="extend-touch-target ml-auto size-8 shadow-none md:size-7"
|
||||
asChild
|
||||
>
|
||||
<Link href={neighbours.previous.url}>
|
||||
@@ -160,7 +166,7 @@ export default async function Page(props: {
|
||||
<MDX components={mdxComponents} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-auto flex h-16 w-full max-w-2xl items-center gap-2 px-4 md:px-0">
|
||||
<div className="mx-auto hidden h-16 w-full max-w-2xl items-center gap-2 px-4 sm:flex md:px-0">
|
||||
{neighbours.previous && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
@@ -187,7 +193,7 @@ export default async function Page(props: {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="sticky top-[calc(var(--header-height)+1px)] z-30 ml-auto hidden h-[calc(100svh-var(--header-height)-var(--footer-height))] w-72 flex-col gap-4 overflow-hidden overscroll-none pb-8 xl:flex">
|
||||
<div className="sticky top-[calc(var(--header-height)+1px)] z-30 ml-auto hidden h-[calc(100svh-var(--footer-height)+2rem)] w-72 flex-col gap-4 overflow-hidden overscroll-none pb-8 xl:flex">
|
||||
<div className="h-(--top-spacing) shrink-0" />
|
||||
{/* @ts-expect-error - revisit fumadocs types. */}
|
||||
{doc.toc?.length ? (
|
||||
|
||||
@@ -75,7 +75,7 @@ export function DataTable<TData, TValue>({
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<DataTableToolbar table={table} />
|
||||
<div className="rounded-md border">
|
||||
<div className="overflow-hidden rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
|
||||
29
apps/v4/app/(app)/llm/[[...slug]]/route.ts
Normal file
29
apps/v4/app/(app)/llm/[[...slug]]/route.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { notFound } from "next/navigation"
|
||||
import { NextResponse, type NextRequest } from "next/server"
|
||||
|
||||
import { source } from "@/lib/source"
|
||||
|
||||
export const revalidate = false
|
||||
|
||||
export async function GET(
|
||||
_req: NextRequest,
|
||||
{ params }: { params: Promise<{ slug: string[] }> }
|
||||
) {
|
||||
const slug = (await params).slug
|
||||
const page = source.getPage(slug)
|
||||
|
||||
if (!page) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
// @ts-expect-error - revisit fumadocs types.
|
||||
return new NextResponse(page.data.content, {
|
||||
headers: {
|
||||
"Content-Type": "text/markdown; charset=utf-8",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function generateStaticParams() {
|
||||
return source.generateParams()
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "react"
|
||||
import { Metadata } from "next"
|
||||
import { notFound } from "next/navigation"
|
||||
import { registryItemSchema } from "shadcn/registry"
|
||||
import { registryItemSchema } from "shadcn/schema"
|
||||
import { z } from "zod"
|
||||
|
||||
import { siteConfig } from "@/lib/config"
|
||||
|
||||
@@ -6,8 +6,8 @@ import { Badge } from "@/registry/new-york-v4/ui/badge"
|
||||
export function Announcement() {
|
||||
return (
|
||||
<Badge asChild variant="secondary" className="rounded-full">
|
||||
<Link href="/docs/components/calendar">
|
||||
New Calendar Component <ArrowRightIcon />
|
||||
<Link href="/docs/registry/namespace">
|
||||
Introducing Namespaced Registries <ArrowRightIcon />
|
||||
</Link>
|
||||
</Badge>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "react"
|
||||
import { registryItemFileSchema } from "shadcn/registry"
|
||||
import { registryItemFileSchema } from "shadcn/schema"
|
||||
import { z } from "zod"
|
||||
|
||||
import { highlightCode } from "@/lib/highlight-code"
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
Terminal,
|
||||
} from "lucide-react"
|
||||
import { ImperativePanelHandle } from "react-resizable-panels"
|
||||
import { registryItemFileSchema, registryItemSchema } from "shadcn/registry"
|
||||
import { registryItemFileSchema, registryItemSchema } from "shadcn/schema"
|
||||
import { z } from "zod"
|
||||
|
||||
import { trackEvent } from "@/lib/events"
|
||||
|
||||
@@ -15,7 +15,7 @@ export function Callout({
|
||||
return (
|
||||
<Alert
|
||||
className={cn(
|
||||
"bg-surface text-surface-foreground mt-6 w-auto border-none md:-mx-4",
|
||||
"bg-surface text-surface-foreground mt-6 w-auto border-none md:-mx-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -209,7 +209,7 @@ export function CardsPayments() {
|
||||
</CardAction>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-4">
|
||||
<div className="rounded-md border">
|
||||
<div className="overflow-hidden rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "react"
|
||||
import { registryItemSchema } from "shadcn/registry"
|
||||
import { registryItemSchema } from "shadcn/schema"
|
||||
import { z } from "zod"
|
||||
|
||||
import { highlightCode } from "@/lib/highlight-code"
|
||||
|
||||
@@ -22,7 +22,7 @@ export function CodeCollapsibleWrapper({
|
||||
<Collapsible
|
||||
open={isOpened}
|
||||
onOpenChange={setIsOpened}
|
||||
className={cn("group/collapsible relative md:-mx-4", className)}
|
||||
className={cn("group/collapsible relative md:-mx-1", className)}
|
||||
{...props}
|
||||
>
|
||||
<CollapsibleTrigger asChild>
|
||||
|
||||
@@ -36,11 +36,13 @@ export function CommandMenu({
|
||||
tree,
|
||||
colors,
|
||||
blocks,
|
||||
navItems,
|
||||
...props
|
||||
}: DialogProps & {
|
||||
tree: typeof source.pageTree
|
||||
colors: ColorPalette[]
|
||||
blocks?: { name: string; description: string; categories: string[] }[]
|
||||
navItems?: { href: string; label: string }[]
|
||||
}) {
|
||||
const router = useRouter()
|
||||
const isMac = useIsMac()
|
||||
@@ -162,12 +164,45 @@ export function CommandMenu({
|
||||
<DialogTitle>Search documentation...</DialogTitle>
|
||||
<DialogDescription>Search for a command to run...</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Command className="**:data-[slot=command-input-wrapper]:bg-input/50 **:data-[slot=command-input-wrapper]:border-input rounded-none bg-transparent **:data-[slot=command-input]:!h-9 **:data-[slot=command-input]:py-0 **:data-[slot=command-input-wrapper]:mb-0 **:data-[slot=command-input-wrapper]:!h-9 **:data-[slot=command-input-wrapper]:rounded-md **:data-[slot=command-input-wrapper]:border">
|
||||
<Command
|
||||
className="**:data-[slot=command-input-wrapper]:bg-input/50 **:data-[slot=command-input-wrapper]:border-input rounded-none bg-transparent **:data-[slot=command-input]:!h-9 **:data-[slot=command-input]:py-0 **:data-[slot=command-input-wrapper]:mb-0 **:data-[slot=command-input-wrapper]:!h-9 **:data-[slot=command-input-wrapper]:rounded-md **:data-[slot=command-input-wrapper]:border"
|
||||
filter={(value, search, keywords) => {
|
||||
const extendValue = value + " " + (keywords?.join(" ") || "")
|
||||
if (extendValue.toLowerCase().includes(search.toLowerCase())) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}}
|
||||
>
|
||||
<CommandInput placeholder="Search documentation..." />
|
||||
<CommandList className="no-scrollbar min-h-80 scroll-pt-2 scroll-pb-1.5">
|
||||
<CommandEmpty className="text-muted-foreground py-12 text-center text-sm">
|
||||
No results found.
|
||||
</CommandEmpty>
|
||||
{navItems && navItems.length > 0 && (
|
||||
<CommandGroup
|
||||
heading="Pages"
|
||||
className="!p-0 [&_[cmdk-group-heading]]:scroll-mt-16 [&_[cmdk-group-heading]]:!p-3 [&_[cmdk-group-heading]]:!pb-1"
|
||||
>
|
||||
{navItems.map((item) => (
|
||||
<CommandMenuItem
|
||||
key={item.href}
|
||||
value={`Navigation ${item.label}`}
|
||||
keywords={["nav", "navigation", item.label.toLowerCase()]}
|
||||
onHighlight={() => {
|
||||
setSelectedType("page")
|
||||
setCopyPayload("")
|
||||
}}
|
||||
onSelect={() => {
|
||||
runCommand(() => router.push(item.href))
|
||||
}}
|
||||
>
|
||||
<IconArrowRight />
|
||||
{item.label}
|
||||
</CommandMenuItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
)}
|
||||
{tree.children.map((group) => (
|
||||
<CommandGroup
|
||||
key={group.$id}
|
||||
|
||||
@@ -51,7 +51,7 @@ export function ComponentPreviewTabs({
|
||||
</Tabs>
|
||||
<div
|
||||
data-tab={tab}
|
||||
className="data-[tab=code]:border-code relative rounded-lg border md:-mx-4"
|
||||
className="data-[tab=code]:border-code relative rounded-lg border md:-mx-1"
|
||||
>
|
||||
<div
|
||||
data-slot="preview"
|
||||
|
||||
@@ -34,7 +34,7 @@ export function ComponentPreview({
|
||||
|
||||
if (type === "block") {
|
||||
return (
|
||||
<div className="relative aspect-[4/2.5] w-full overflow-hidden rounded-md border md:-mx-4">
|
||||
<div className="relative aspect-[4/2.5] w-full overflow-hidden rounded-md border md:-mx-1">
|
||||
<Image
|
||||
src={`/r/styles/new-york-v4/${name}-light.png`}
|
||||
alt={name}
|
||||
|
||||
@@ -1,33 +1,156 @@
|
||||
"use client"
|
||||
|
||||
import { IconCheck, IconCopy } from "@tabler/icons-react"
|
||||
import { IconCheck, IconChevronDown, IconCopy } from "@tabler/icons-react"
|
||||
|
||||
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
|
||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/registry/new-york-v4/ui/tooltip"
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/registry/new-york-v4/ui/dropdown-menu"
|
||||
import {
|
||||
Popover,
|
||||
PopoverAnchor,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/registry/new-york-v4/ui/popover"
|
||||
import { Separator } from "@/registry/new-york-v4/ui/separator"
|
||||
|
||||
export function DocsCopyPage({ page }: { page: string }) {
|
||||
function getPromptUrl(baseURL: string, url: string) {
|
||||
return `${baseURL}?q=${encodeURIComponent(
|
||||
`I’m looking at this shadcn/ui documentation: ${url}.
|
||||
Help me understand how to use it. Be ready to explain concepts, give examples, or help debug based on it.
|
||||
`
|
||||
)}`
|
||||
}
|
||||
|
||||
const menuItems = {
|
||||
markdown: (url: string) => (
|
||||
<a href={`${url}.md`} target="_blank" rel="noopener noreferrer">
|
||||
<svg strokeLinejoin="round" viewBox="0 0 22 16">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M19.5 2.25H2.5C1.80964 2.25 1.25 2.80964 1.25 3.5V12.5C1.25 13.1904 1.80964 13.75 2.5 13.75H19.5C20.1904 13.75 20.75 13.1904 20.75 12.5V3.5C20.75 2.80964 20.1904 2.25 19.5 2.25ZM2.5 1C1.11929 1 0 2.11929 0 3.5V12.5C0 13.8807 1.11929 15 2.5 15H19.5C20.8807 15 22 13.8807 22 12.5V3.5C22 2.11929 20.8807 1 19.5 1H2.5ZM3 4.5H4H4.25H4.6899L4.98715 4.82428L7 7.02011L9.01285 4.82428L9.3101 4.5H9.75H10H11V5.5V11.5H9V7.79807L7.73715 9.17572L7 9.97989L6.26285 9.17572L5 7.79807V11.5H3V5.5V4.5ZM15 8V4.5H17V8H19.5L17 10.5L16 11.5L15 10.5L12.5 8H15Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
View as Markdown
|
||||
</a>
|
||||
),
|
||||
v0: (url: string) => (
|
||||
<a
|
||||
href={getPromptUrl("https://v0.dev", url)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 147 70"
|
||||
className="size-4.5 -translate-x-px"
|
||||
>
|
||||
<path d="M56 50.203V14h14v46.156C70 65.593 65.593 70 60.156 70c-2.596 0-5.158-1-7-2.843L0 14h19.797L56 50.203ZM147 56h-14V23.953L100.953 56H133v14H96.687C85.814 70 77 61.186 77 50.312V14h14v32.156L123.156 14H91V0h36.312C138.186 0 147 8.814 147 19.688V56Z" />
|
||||
</svg>
|
||||
<span className="-translate-x-[2px]">Open in v0</span>
|
||||
</a>
|
||||
),
|
||||
chatgpt: (url: string) => (
|
||||
<a
|
||||
href={getPromptUrl("https://chatgpt.com", url)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M22.282 9.821a5.985 5.985 0 0 0-.516-4.91 6.046 6.046 0 0 0-6.51-2.9A6.065 6.065 0 0 0 4.981 4.18a5.985 5.985 0 0 0-3.998 2.9 6.046 6.046 0 0 0 .743 7.097 5.98 5.98 0 0 0 .51 4.911 6.051 6.051 0 0 0 6.515 2.9A5.985 5.985 0 0 0 13.26 24a6.056 6.056 0 0 0 5.772-4.206 5.99 5.99 0 0 0 3.997-2.9 6.056 6.056 0 0 0-.747-7.073zM13.26 22.43a4.476 4.476 0 0 1-2.876-1.04l.141-.081 4.779-2.758a.795.795 0 0 0 .392-.681v-6.737l2.02 1.168a.071.071 0 0 1 .038.052v5.583a4.504 4.504 0 0 1-4.494 4.494zM3.6 18.304a4.47 4.47 0 0 1-.535-3.014l.142.085 4.783 2.759a.771.771 0 0 0 .78 0l5.843-3.369v2.332a.08.08 0 0 1-.033.062L9.74 19.95a4.5 4.5 0 0 1-6.14-1.646zM2.34 7.896a4.485 4.485 0 0 1 2.366-1.973V11.6a.766.766 0 0 0 .388.676l5.815 3.355-2.02 1.168a.076.076 0 0 1-.071 0l-4.83-2.786A4.504 4.504 0 0 1 2.34 7.872zm16.597 3.855-5.833-3.387L15.119 7.2a.076.076 0 0 1 .071 0l4.83 2.791a4.494 4.494 0 0 1-.676 8.105v-5.678a.79.79 0 0 0-.407-.667zm2.01-3.023-.141-.085-4.774-2.782a.776.776 0 0 0-.785 0L9.409 9.23V6.897a.066.066 0 0 1 .028-.061l4.83-2.787a4.5 4.5 0 0 1 6.68 4.66zm-12.64 4.135-2.02-1.164a.08.08 0 0 1-.038-.057V6.075a4.5 4.5 0 0 1 7.375-3.453l-.142.08-4.778 2.758a.795.795 0 0 0-.393.681zm1.097-2.365 2.602-1.5 2.607 1.5v2.999l-2.597 1.5-2.607-1.5Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
Open in ChatGPT
|
||||
</a>
|
||||
),
|
||||
claude: (url: string) => (
|
||||
<a
|
||||
href={getPromptUrl("https://claude.ai/new", url)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="m4.714 15.956 4.718-2.648.079-.23-.08-.128h-.23l-.79-.048-2.695-.073-2.337-.097-2.265-.122-.57-.121-.535-.704.055-.353.48-.321.685.06 1.518.104 2.277.157 1.651.098 2.447.255h.389l.054-.158-.133-.097-.103-.098-2.356-1.596-2.55-1.688-1.336-.972-.722-.491L2 6.223l-.158-1.008.655-.722.88.06.225.061.893.686 1.906 1.476 2.49 1.833.364.304.146-.104.018-.072-.164-.274-1.354-2.446-1.445-2.49-.644-1.032-.17-.619a2.972 2.972 0 0 1-.103-.729L6.287.133 6.7 0l.995.134.42.364.619 1.415L9.735 4.14l1.555 3.03.455.898.243.832.09.255h.159V9.01l.127-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.583.28.48.685-.067.444-.286 1.851-.558 2.903-.365 1.942h.213l.243-.242.983-1.306 1.652-2.064.728-.82.85-.904.547-.431h1.032l.759 1.129-.34 1.166-1.063 1.347-.88 1.142-1.263 1.7-.79 1.36.074.11.188-.02 2.853-.606 1.542-.28 1.84-.315.832.388.09.395-.327.807-1.967.486-2.307.462-3.436.813-.043.03.049.061 1.548.146.662.036h1.62l3.018.225.79.522.473.638-.08.485-1.213.62-1.64-.389-3.825-.91-1.31-.329h-.183v.11l1.093 1.068 2.003 1.81 2.508 2.33.127.578-.321.455-.34-.049-2.204-1.657-.85-.747-1.925-1.62h-.127v.17l.443.649 2.343 3.521.122 1.08-.17.353-.607.213-.668-.122-1.372-1.924-1.415-2.168-1.141-1.943-.14.08-.674 7.254-.316.37-.728.28-.607-.461-.322-.747.322-1.476.388-1.924.316-1.53.285-1.9.17-.632-.012-.042-.14.018-1.432 1.967-2.18 2.945-1.724 1.845-.413.164-.716-.37.066-.662.401-.589 2.386-3.036 1.439-1.882.929-1.086-.006-.158h-.055L4.138 18.56l-1.13.146-.485-.456.06-.746.231-.243 1.907-1.312Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
Open in Claude
|
||||
</a>
|
||||
),
|
||||
}
|
||||
|
||||
export function DocsCopyPage({ page, url }: { page: string; url: string }) {
|
||||
const { copyToClipboard, isCopied } = useCopyToClipboard()
|
||||
|
||||
const trigger = (
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className="peer -ml-0.5 size-8 shadow-none md:size-7 md:text-[0.8rem]"
|
||||
>
|
||||
<IconChevronDown className="rotate-180 sm:rotate-0" />
|
||||
</Button>
|
||||
)
|
||||
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Popover>
|
||||
<div className="bg-secondary group/buttons relative flex rounded-lg *:[[data-slot=button]]:focus-visible:relative *:[[data-slot=button]]:focus-visible:z-10">
|
||||
<PopoverAnchor />
|
||||
<Button
|
||||
variant="outline"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className="h-8 pl-1.5 md:h-7 [&>svg]:size-3.5"
|
||||
className="h-8 shadow-none md:h-7 md:text-[0.8rem]"
|
||||
onClick={() => copyToClipboard(page)}
|
||||
>
|
||||
{isCopied ? <IconCheck /> : <IconCopy />} Copy Page
|
||||
{isCopied ? <IconCheck /> : <IconCopy />}
|
||||
Copy Page
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Copy as Markdown</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild className="hidden sm:flex">
|
||||
{trigger}
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="shadow-none">
|
||||
{Object.entries(menuItems).map(([key, value]) => (
|
||||
<DropdownMenuItem key={key} asChild>
|
||||
{value(url)}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<Separator
|
||||
orientation="vertical"
|
||||
className="!bg-foreground/10 absolute top-0 right-8 z-0 !h-8 peer-focus-visible:opacity-0 sm:right-7 sm:!h-7"
|
||||
/>
|
||||
<PopoverTrigger asChild className="flex sm:hidden">
|
||||
{trigger}
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
className="bg-background/70 dark:bg-background/60 w-52 !origin-center rounded-lg p-1 shadow-sm backdrop-blur-sm"
|
||||
align="start"
|
||||
>
|
||||
{Object.entries(menuItems).map(([key, value]) => (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="lg"
|
||||
asChild
|
||||
key={key}
|
||||
className="*:[svg]:text-muted-foreground w-full justify-start text-base font-normal"
|
||||
>
|
||||
{value(url)}
|
||||
</Button>
|
||||
))}
|
||||
</PopoverContent>
|
||||
</div>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,24 @@ import {
|
||||
SidebarMenuItem,
|
||||
} from "@/registry/new-york-v4/ui/sidebar"
|
||||
|
||||
const TOP_LEVEL_SECTIONS = [
|
||||
{ name: "Get Started", href: "/docs" },
|
||||
{
|
||||
name: "Components",
|
||||
href: "/docs/components",
|
||||
},
|
||||
{
|
||||
name: "Registry",
|
||||
href: "/docs/registry",
|
||||
},
|
||||
{
|
||||
name: "MCP Server",
|
||||
href: "/docs/mcp",
|
||||
},
|
||||
]
|
||||
const EXCLUDED_SECTIONS = ["installation", "dark-mode"]
|
||||
const EXCLUDED_PAGES = ["/docs"]
|
||||
|
||||
export function DocsSidebar({
|
||||
tree,
|
||||
...props
|
||||
@@ -23,40 +41,79 @@ export function DocsSidebar({
|
||||
|
||||
return (
|
||||
<Sidebar
|
||||
className="sticky top-[calc(var(--header-height)+1px)] z-30 hidden h-[calc(100svh-var(--header-height)-var(--footer-height))] bg-transparent lg:flex"
|
||||
className="sticky top-[calc(var(--header-height)+1px)] z-30 hidden h-[calc(100svh-var(--footer-height)+2rem)] bg-transparent lg:flex"
|
||||
collapsible="none"
|
||||
{...props}
|
||||
>
|
||||
<SidebarContent className="no-scrollbar px-2 pb-12">
|
||||
<SidebarContent className="no-scrollbar overflow-x-hidden px-2 pb-12">
|
||||
<div className="h-(--top-spacing) shrink-0" />
|
||||
{tree.children.map((item) => (
|
||||
<SidebarGroup key={item.$id}>
|
||||
<SidebarGroupLabel className="text-muted-foreground font-medium">
|
||||
{item.name}
|
||||
</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
{item.type === "folder" && (
|
||||
<SidebarMenu className="gap-0.5">
|
||||
{item.children.map((item) => {
|
||||
return (
|
||||
item.type === "page" && (
|
||||
<SidebarMenuItem key={item.url}>
|
||||
<SidebarMenuButton
|
||||
asChild
|
||||
isActive={item.url === pathname}
|
||||
className="data-[active=true]:bg-accent data-[active=true]:border-accent 3xl:fixed:w-full 3xl:fixed:max-w-48 relative h-[30px] w-fit overflow-visible border border-transparent text-[0.8rem] font-medium after:absolute after:inset-x-0 after:-inset-y-1 after:z-0 after:rounded-md"
|
||||
>
|
||||
<Link href={item.url}>{item.name}</Link>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel className="text-muted-foreground font-medium">
|
||||
Sections
|
||||
</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
{TOP_LEVEL_SECTIONS.map(({ name, href }) => {
|
||||
return (
|
||||
<SidebarMenuItem key={name}>
|
||||
<SidebarMenuButton
|
||||
asChild
|
||||
isActive={
|
||||
href === "/docs"
|
||||
? pathname === href
|
||||
: pathname.startsWith(href)
|
||||
}
|
||||
className="data-[active=true]:bg-accent data-[active=true]:border-accent 3xl:fixed:w-full 3xl:fixed:max-w-48 relative h-[30px] w-fit overflow-visible border border-transparent text-[0.8rem] font-medium after:absolute after:inset-x-0 after:-inset-y-1 after:z-0 after:rounded-md"
|
||||
>
|
||||
<Link href={href}>
|
||||
<span className="absolute inset-0 flex w-(--sidebar-width) bg-transparent" />
|
||||
{name}
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
)
|
||||
})}
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
{tree.children.map((item) => {
|
||||
if (EXCLUDED_SECTIONS.includes(item.$id ?? "")) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<SidebarGroup key={item.$id}>
|
||||
<SidebarGroupLabel className="text-muted-foreground font-medium">
|
||||
{item.name}
|
||||
</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
{item.type === "folder" && (
|
||||
<SidebarMenu className="gap-0.5">
|
||||
{item.children.map((item) => {
|
||||
return (
|
||||
item.type === "page" &&
|
||||
!EXCLUDED_PAGES.includes(item.url) && (
|
||||
<SidebarMenuItem key={item.url}>
|
||||
<SidebarMenuButton
|
||||
asChild
|
||||
isActive={item.url === pathname}
|
||||
className="data-[active=true]:bg-accent data-[active=true]:border-accent 3xl:fixed:w-full 3xl:fixed:max-w-48 relative h-[30px] w-fit overflow-visible border border-transparent text-[0.8rem] font-medium after:absolute after:inset-x-0 after:-inset-y-1 after:z-0 after:rounded-md"
|
||||
>
|
||||
<Link href={item.url}>
|
||||
<span className="absolute inset-0 flex w-(--sidebar-width) bg-transparent" />
|
||||
{item.name}
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
)
|
||||
)
|
||||
)
|
||||
})}
|
||||
</SidebarMenu>
|
||||
)}
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
))}
|
||||
})}
|
||||
</SidebarMenu>
|
||||
)}
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
)
|
||||
})}
|
||||
</SidebarContent>
|
||||
</Sidebar>
|
||||
)
|
||||
|
||||
@@ -13,6 +13,22 @@ import {
|
||||
PopoverTrigger,
|
||||
} from "@/registry/new-york-v4/ui/popover"
|
||||
|
||||
const TOP_LEVEL_SECTIONS = [
|
||||
{ name: "Get Started", href: "/docs" },
|
||||
{
|
||||
name: "Components",
|
||||
href: "/docs/components",
|
||||
},
|
||||
{
|
||||
name: "Registry",
|
||||
href: "/docs/registry",
|
||||
},
|
||||
{
|
||||
name: "MCP Server",
|
||||
href: "/docs/mcp",
|
||||
},
|
||||
]
|
||||
|
||||
export function MobileNav({
|
||||
tree,
|
||||
items,
|
||||
@@ -79,6 +95,18 @@ export function MobileNav({
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="text-muted-foreground text-sm font-medium">
|
||||
Sections
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
{TOP_LEVEL_SECTIONS.map(({ name, href }) => (
|
||||
<MobileLink key={name} href={href} onOpenChange={setOpen}>
|
||||
{name}
|
||||
</MobileLink>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-8">
|
||||
{tree?.children?.map((group, index) => {
|
||||
if (group.type === "folder") {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Icons } from "@/components/icons"
|
||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
||||
|
||||
// v0 uses the default style.
|
||||
const V0_STYLE = "default"
|
||||
const V0_STYLE = "new-york-v4"
|
||||
|
||||
export function OpenInV0Button({
|
||||
name,
|
||||
|
||||
@@ -13,7 +13,7 @@ export function OpenInV0Cta({ className }: React.ComponentProps<"div">) {
|
||||
Deploy your shadcn/ui app on Vercel
|
||||
</div>
|
||||
<div className="text-muted-foreground">
|
||||
Trusted by OpenAI, Sonos, Chick-fil-A, and more.
|
||||
Trusted by OpenAI, Sonos, Adobe, and more.
|
||||
</div>
|
||||
<div className="text-muted-foreground">
|
||||
Vercel provides tools and infrastructure to deploy apps and features at
|
||||
|
||||
@@ -2,10 +2,10 @@ import { siteConfig } from "@/lib/config"
|
||||
|
||||
export function SiteFooter() {
|
||||
return (
|
||||
<footer className="group-has-[.section-soft]/body:bg-surface/40 3xl:fixed:bg-transparent dark:bg-transparent">
|
||||
<footer className="group-has-[.section-soft]/body:bg-surface/40 3xl:fixed:bg-transparent group-has-[.docs-nav]/body:pb-20 group-has-[.docs-nav]/body:sm:pb-0 dark:bg-transparent">
|
||||
<div className="container-wrapper px-4 xl:px-6">
|
||||
<div className="flex h-(--footer-height) items-center justify-between">
|
||||
<div className="text-muted-foreground w-full text-center text-xs leading-loose sm:text-sm">
|
||||
<div className="text-muted-foreground w-full px-1 text-center text-xs leading-loose sm:text-sm">
|
||||
Built by{" "}
|
||||
<a
|
||||
href={siteConfig.links.twitter}
|
||||
|
||||
@@ -41,7 +41,11 @@ export function SiteHeader() {
|
||||
<MainNav items={siteConfig.navItems} className="hidden lg:flex" />
|
||||
<div className="ml-auto flex items-center gap-2 md:flex-1 md:justify-end">
|
||||
<div className="hidden w-full flex-1 md:flex md:w-auto md:flex-none">
|
||||
<CommandMenu tree={pageTree} colors={colors} />
|
||||
<CommandMenu
|
||||
tree={pageTree}
|
||||
colors={colors}
|
||||
navItems={siteConfig.navItems}
|
||||
/>
|
||||
</div>
|
||||
<Separator
|
||||
orientation="vertical"
|
||||
|
||||
@@ -4,6 +4,52 @@ description: Latest updates and announcements.
|
||||
toc: false
|
||||
---
|
||||
|
||||
## July 2025 - Universal Registry Items
|
||||
|
||||
We've added support for universal registry items. This allows you to create registry items that can be distributed to any project i.e. no framework, no components.json, no tailwind, no react required.
|
||||
|
||||
This new registry item type unlocks a lot of new workflows. You can now distribute code, config, rules, docs, anything to any code project.
|
||||
|
||||
See the [docs](/docs/registry/examples) for more details and examples.
|
||||
|
||||
## July 2025 - Local File Support
|
||||
|
||||
The shadcn CLI now supports local files. Initialize projects and add components, themes, hooks, utils and more from local JSON files.
|
||||
|
||||
```bash
|
||||
# Initialize a project from a local file
|
||||
npx shadcn init ./template.json
|
||||
|
||||
# Add a component from a local file
|
||||
npx shadcn add ./block.json
|
||||
```
|
||||
|
||||
This feature enables powerful new workflows:
|
||||
|
||||
- **Zero setup** - No remote registries required
|
||||
- **Faster development** - Test registry items locally before publishing
|
||||
- **Enhanced workflow for agents and MCP** - Generate and run registry items locally
|
||||
- **Private components** - Keep proprietary components local and private.
|
||||
|
||||
## June 2025 - `radix-ui`
|
||||
|
||||
We've added a new command to migrate to the new `radix-ui` package. This command will replace all `@radix-ui/react-*` imports with `radix-ui`.
|
||||
|
||||
```bash
|
||||
npx shadcn@latest migrate radix
|
||||
```
|
||||
|
||||
It will automatically update all imports in your `ui` components and install `radix-ui` as a dependency.
|
||||
|
||||
```diff showLineNumbers title="components/ui/alert-dialog.tsx"
|
||||
- import * as AlertDialogPrimitive from "@radix-ui/react-dialog"
|
||||
+ import { AlertDialog as AlertDialogPrimitive } from "radix-ui"
|
||||
```
|
||||
|
||||
Make sure to test your components and project after running the command.
|
||||
|
||||
**Note:** To update imports for newly added components, run the migration command again.
|
||||
|
||||
## June 2025 - Calendar Component
|
||||
|
||||
We've upgraded the `Calendar` component to the latest version of [React DayPicker](https://daypicker.dev).
|
||||
@@ -42,9 +88,9 @@ Learn more in the thread here: https://x.com/shadcn/status/1917597228513853603
|
||||
|
||||
We tagged shadcn 2.5.0 earlier this week. It comes with a pretty cool feature: **resolve anywhere**.
|
||||
|
||||
Registries can now place files anywhere in an app and we’ll properly resolve imports. No need to stick to a fixed file structure. It can even add files outside the registry itself.
|
||||
Registries can now place files anywhere in an app and we'll properly resolve imports. No need to stick to a fixed file structure. It can even add files outside the registry itself.
|
||||
|
||||
On install, we track all files and perform a multi-pass resolution to correctly handle imports and aliases. It’s fast.
|
||||
On install, we track all files and perform a multi-pass resolution to correctly handle imports and aliases. It's fast.
|
||||
|
||||
## March 2025 - Cross-framework Route Support
|
||||
|
||||
@@ -61,7 +107,7 @@ What's New:
|
||||
- The CLI can now initialize projects with Tailwind v4.
|
||||
- Full support for the new @theme directive and @theme inline option.
|
||||
- All components are updated for Tailwind v4 and React 19.
|
||||
- We’ve removed the forwardRefs and adjusted the types.
|
||||
- We've removed the forwardRefs and adjusted the types.
|
||||
- Every primitive now has a data-slot attribute for styling.
|
||||
- We've fixed and cleaned up the style of the components.
|
||||
- We're deprecating the toast component in favor of sonner.
|
||||
@@ -139,7 +185,7 @@ The new CLI is now available. It's a complete rewrite with a lot of new features
|
||||
This is a major step towards distributing code that you and your LLMs can access and use.
|
||||
|
||||
1. First up, the cli now has support for all major React framework out of the box. Next.js, Remix, Vite and Laravel. And when you init into a new app, we update your existing Tailwind files instead of overriding.
|
||||
2. A component now ship its own dependencies. Take the accordion for example, it can define its Tailwind keyframes. When you add it to your project, we’ll update your tailwind.config.ts file accordingly.
|
||||
2. A component now ship its own dependencies. Take the accordion for example, it can define its Tailwind keyframes. When you add it to your project, we'll update your tailwind.config.ts file accordingly.
|
||||
3. You can also install remote components using url. `npx shadcn add https://acme.com/registry/navbar.json`.
|
||||
4. We have also improve the init command. It does framework detection and can even init a brand new Next.js app in one command. `npx shadcn init`.
|
||||
5. We have created a new schema that you can use to ship your own component registry. And since it has support for urls, you can even use it to distribute private components.
|
||||
|
||||
@@ -21,15 +21,14 @@ Usage: shadcn init [options] [components...]
|
||||
initialize your project and install dependencies
|
||||
|
||||
Arguments:
|
||||
components the components to add or a url to the component.
|
||||
components name, url or local path to component
|
||||
|
||||
Options:
|
||||
-t, --template <template> the template to use. (next, next-monorepo)
|
||||
-b, --base-color <base-color> the base color to use. (neutral, gray, zinc, stone, slate)
|
||||
-y, --yes skip confirmation prompt. (default: true)
|
||||
-f, --force force overwrite of existing configuration. (default: false)
|
||||
-c, --cwd <cwd> the working directory. defaults to the current directory. (default:
|
||||
"/Users/shadcn/Code/shadcn/ui/packages/shadcn")
|
||||
-c, --cwd <cwd> the working directory. defaults to the current directory.
|
||||
-s, --silent mute output. (default: false)
|
||||
--src-dir use the src directory when creating a new project. (default: false)
|
||||
--no-src-dir do not use the src directory when creating a new project.
|
||||
@@ -54,12 +53,12 @@ Usage: shadcn add [options] [components...]
|
||||
add a component to your project
|
||||
|
||||
Arguments:
|
||||
components the components to add or a url to the component.
|
||||
components name, url or local path to component
|
||||
|
||||
Options:
|
||||
-y, --yes skip confirmation prompt. (default: false)
|
||||
-o, --overwrite overwrite existing files. (default: false)
|
||||
-c, --cwd <cwd> the working directory. defaults to the current directory. (default: "/Users/shadcn/Desktop")
|
||||
-c, --cwd <cwd> the working directory. defaults to the current directory.
|
||||
-a, --all add all available components (default: false)
|
||||
-p, --path <path> the path to add the component to.
|
||||
-s, --silent mute output. (default: false)
|
||||
@@ -92,8 +91,7 @@ Arguments:
|
||||
|
||||
Options:
|
||||
-o, --output <path> destination directory for json files (default: "./public/r")
|
||||
-c, --cwd <cwd> the working directory. defaults to the current directory. (default:
|
||||
"/Users/shadcn/Code/shadcn/ui/packages/shadcn")
|
||||
-c, --cwd <cwd> the working directory. defaults to the current directory.
|
||||
-h, --help display help for command
|
||||
```
|
||||
|
||||
|
||||
@@ -210,3 +210,93 @@ Import alias for `hooks` such as `use-media-query` or `use-toast`.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## registries
|
||||
|
||||
Configure multiple resource registries for your project. This allows you to install components, libraries, utilities, and other resources from various sources including private registries.
|
||||
|
||||
See the <Link href="/docs/registry/namespace">Namespaced Registries</Link> documentation for detailed information.
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
Configure registries with URL templates:
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"registries": {
|
||||
"@v0": "https://v0.dev/chat/b/{name}",
|
||||
"@acme": "https://registry.acme.com/{name}.json",
|
||||
"@internal": "https://internal.company.com/{name}.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `{name}` placeholder is replaced with the resource name when installing.
|
||||
|
||||
### Advanced Configuration with Authentication
|
||||
|
||||
For private registries that require authentication:
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"registries": {
|
||||
"@private": {
|
||||
"url": "https://api.company.com/registry/{name}.json",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${REGISTRY_TOKEN}",
|
||||
"X-API-Key": "${API_KEY}"
|
||||
},
|
||||
"params": {
|
||||
"version": "latest"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Environment variables in the format `${VAR_NAME}` are automatically expanded from your environment.
|
||||
|
||||
### Using Namespaced Registries
|
||||
|
||||
Once configured, install resources using the namespace syntax:
|
||||
|
||||
```bash
|
||||
# Install from a configured registry
|
||||
npx shadcn@latest add @v0/dashboard
|
||||
|
||||
# Install from private registry
|
||||
npx shadcn@latest add @private/button
|
||||
|
||||
# Install multiple resources
|
||||
npx shadcn@latest add @acme/header @internal/auth-utils
|
||||
```
|
||||
|
||||
### Example: Multiple Registry Setup
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"registries": {
|
||||
"@shadcn": "https://ui.shadcn.com/r/{name}.json",
|
||||
"@company-ui": {
|
||||
"url": "https://registry.company.com/ui/{name}.json",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${COMPANY_TOKEN}"
|
||||
}
|
||||
},
|
||||
"@team": {
|
||||
"url": "https://team.company.com/{name}.json",
|
||||
"params": {
|
||||
"team": "frontend",
|
||||
"version": "${REGISTRY_VERSION}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This configuration allows you to:
|
||||
- Install public components from shadcn/ui
|
||||
- Access private company UI components with authentication
|
||||
- Use team-specific resources with versioning
|
||||
|
||||
For more information about authentication, see the <Link href="/docs/registry/authentication">Authentication</Link> documentation.
|
||||
|
||||
@@ -15,3 +15,4 @@ description: Every component recreated in Figma. With customizable props, typogr
|
||||
## Free
|
||||
|
||||
- [shadcn/ui design system](https://www.figma.com/community/file/1203061493325953101) by [Pietro Schirano](https://twitter.com/skirano) - A design companion for shadcn/ui. Each component was painstakingly crafted to perfectly match the code implementation.
|
||||
- [Obra shadcn/ui](https://www.figma.com/community/file/1514746685758799870/obra-shadcn-ui) by [Obra Studio](https://obra.studio/) - Carefully crafted kit designed in the philosophy of shadcn, tracks v4, MIT licensed
|
||||
|
||||
267
apps/v4/content/docs/(root)/mcp.mdx
Normal file
267
apps/v4/content/docs/(root)/mcp.mdx
Normal file
@@ -0,0 +1,267 @@
|
||||
---
|
||||
title: MCP Server
|
||||
description: Use the shadcn MCP server to browse, search, and install components from registries.
|
||||
---
|
||||
|
||||
The shadcn MCP Server allows AI assistants to interact with items from registries. You can browse available components, search for specific ones, and install them directly into your project using natural language.
|
||||
|
||||
For example, you can ask an AI assistant to "Build a landing page using components from the acme registry" or "Find me a login form from the shadcn registry".
|
||||
|
||||
Registries are configured in your project's `components.json` file.
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"registries": {
|
||||
"@acme": "https://acme.com/r/{name}.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
Select your MCP client and follow the instructions to configure the shadcn MCP server. If you'd like to do it manually, see the [Configuration](#configuration) section.
|
||||
|
||||
<Tabs defaultValue="claude">
|
||||
<TabsList>
|
||||
<TabsTrigger value="claude">Claude Code</TabsTrigger>
|
||||
<TabsTrigger value="cursor">Cursor</TabsTrigger>
|
||||
<TabsTrigger value="vscode">VS Code</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="claude" className="mt-4">
|
||||
**Run the following command** in your project:
|
||||
```bash
|
||||
npx shadcn@latest mcp init --client claude
|
||||
```
|
||||
|
||||
**Restart Claude Code** and try the following prompts:
|
||||
- Show me all available components in the shadcn registry
|
||||
- Add the button, dialog and card components to my project
|
||||
- Create a contact form using components from the shadcn registry
|
||||
|
||||
**Note:** You can use `/mcp` command in Claude Code to debug the MCP server.
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="cursor" className="mt-4">
|
||||
**Run the following command** in your project:
|
||||
```bash
|
||||
npx shadcn@latest mcp init --client cursor
|
||||
```
|
||||
|
||||
Open **Cursor Settings** and **Enable the MCP server** for shadcn. Then try the following prompts:
|
||||
- Show me all available components in the shadcn registry
|
||||
- Add the button, dialog and card components to my project
|
||||
- Create a contact form using components from the shadcn registry
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="vscode" className="mt-4">
|
||||
**Run the following command** in your project:
|
||||
```bash
|
||||
npx shadcn@latest mcp init --client vscode
|
||||
```
|
||||
|
||||
Open `.vscode/mcp.json` and click **Start** next to the shadcn server. Then try the following prompts with GitHub Copilot:
|
||||
- Show me all available components in the shadcn registry
|
||||
- Add the button, dialog and card components to my project
|
||||
- Create a contact form using components from the shadcn registry
|
||||
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## What is MCP?
|
||||
|
||||
[Model Context Protocol (MCP)](https://modelcontextprotocol.io) is an open protocol that enables AI assistants to securely connect to external data sources and tools. With the shadcn MCP server, your AI assistant gains direct access to:
|
||||
|
||||
- **Browse Components** - List all available components, blocks, and templates from any configured registry
|
||||
- **Search Across Registries** - Find specific components by name or functionality across multiple sources
|
||||
- **Install with Natural Language** - Add components using simple conversational prompts like "add a login form"
|
||||
- **Support for Multiple Registries** - Access public registries, private company libraries, and third-party sources
|
||||
|
||||
## How It Works
|
||||
|
||||
The MCP server acts as a bridge between your AI assistant, component registries and the shadcn CLI.
|
||||
|
||||
1. **Registry Connection** - MCP connects to configured registries (shadcn/ui, private registries, third-party sources)
|
||||
2. **Natural Language** - You describe what you need in plain English
|
||||
3. **AI Processing** - The assistant translates your request into registry commands
|
||||
4. **Component Delivery** - Resources are fetched and installed in your project
|
||||
|
||||
### Supported Registries
|
||||
|
||||
The shadcn MCP server works out of the box with any shadcn-compatible registry.
|
||||
|
||||
- **shadcn/ui Registry** - The default registry with all shadcn/ui components
|
||||
- **Private Registries** - Your company's internal component libraries
|
||||
- **Third-Party Registries** - Any registry following the shadcn registry specification
|
||||
- **Namespaced Registries** - Multiple registries configured with `@namespace` syntax
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
The shadcn MCP server works with any MCP client. Here are the instructions for the most popular ones.
|
||||
|
||||
### Claude Code
|
||||
|
||||
To use the shadcn MCP server with Claude Code, add the following configuration to your project's `.mcp.json` file:
|
||||
|
||||
```json title=".mcp.json" showLineNumbers
|
||||
{
|
||||
"mcpServers": {
|
||||
"shadcn": {
|
||||
"command": "npx",
|
||||
"args": ["shadcn@latest", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After adding the configuration, restart Claude Code and run `/mcp` to see the shadcn MCP server in the list. If you see `Connected`, you're good to go.
|
||||
|
||||
See the [Claude Code MCP documentation](https://docs.anthropic.com/en/docs/claude-code/mcp) for more details.
|
||||
|
||||
### Cursor
|
||||
|
||||
To configure MCP in Cursor, add the shadcn server to your project's `.cursor/mcp.json` configuration file:
|
||||
|
||||
```json title=".cursor/mcp.json" showLineNumbers
|
||||
{
|
||||
"mcpServers": {
|
||||
"shadcn": {
|
||||
"command": "npx",
|
||||
"args": ["shadcn@latest", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After adding the configuration, enable the shadcn MCP server in Cursor Settings.
|
||||
|
||||
Once enabled, you should see a green dot next to the shadcn server in the MCP server list and a list of available tools.
|
||||
|
||||
See the [Cursor MCP documentation](https://docs.cursor.com/en/context/mcp#using-mcp-json) for more details.
|
||||
|
||||
### VS Code
|
||||
|
||||
To configure MCP in VS Code with GitHub Copilot, add the shadcn server to your project's `.vscode/mcp.json` configuration file:
|
||||
|
||||
```json title=".vscode/mcp.json" showLineNumbers
|
||||
{
|
||||
"mcpServers": {
|
||||
"shadcn": {
|
||||
"command": "npx",
|
||||
"args": ["shadcn@latest", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After adding the configuration, open `.vscode/mcp.json` and click **Start** next to the shadcn server.
|
||||
|
||||
See the [VS Code MCP documentation](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) for more details.
|
||||
|
||||
---
|
||||
|
||||
## Configuring Registries
|
||||
|
||||
The MCP server supports multiple registries through your project's `components.json` configuration. This allows you to access components from various sources including private registries and third-party providers.
|
||||
|
||||
<Callout>
|
||||
**Note:** No configuration is needed to access standard shadcn/ui registry.
|
||||
</Callout>
|
||||
|
||||
Configure additional registries in your `components.json`:
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"registries": {
|
||||
"@acme": "https://registry.acme.com/{name}.json",
|
||||
"@internal": {
|
||||
"url": "https://internal.company.com/{name}.json",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${REGISTRY_TOKEN}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Authentication
|
||||
|
||||
For private registries requiring authentication, set environment variables in your `.env.local`:
|
||||
|
||||
```bash title=".env.local"
|
||||
REGISTRY_TOKEN=your_token_here
|
||||
API_KEY=your_api_key_here
|
||||
```
|
||||
|
||||
For more details on registry configuration, see the [Namespaces documentation](/docs/registry/namespace).
|
||||
|
||||
---
|
||||
|
||||
## Example Prompts
|
||||
|
||||
Once MCP is configured, you can use natural language to interact with registries:
|
||||
|
||||
### Browse & Search
|
||||
|
||||
- "Show me all available components in the shadcn registry"
|
||||
- "Find me a login form from the shadcn registry"
|
||||
|
||||
### Install Components
|
||||
|
||||
- "Add the button component to my project"
|
||||
- "Create a login form using shadcn components"
|
||||
|
||||
### Work with Namespaces
|
||||
|
||||
- "Show me components from @acme registry"
|
||||
- "Install @internal/auth-form"
|
||||
- "Build me a landing page using hero, features and testimonials components from the @acme registry"
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### MCP Not Responding
|
||||
|
||||
If the MCP server isn't responding to prompts:
|
||||
|
||||
1. **Check Configuration** - Verify the MCP server is properly configured and enabled in your MCP client
|
||||
2. **Restart MCP Client** - Restart your MCP client after configuration changes
|
||||
3. **Verify Installation** - Ensure `shadcn` is installed in your project
|
||||
4. **Check Network** - Confirm you can access the configured registries
|
||||
|
||||
### Registry Access Issues
|
||||
|
||||
If components aren't loading from registries:
|
||||
|
||||
1. **Check components.json** - Verify registry URLs are correct
|
||||
2. **Test Authentication** - Ensure environment variables are set for private registries
|
||||
3. **Verify Registry** - Confirm the registry is online and accessible
|
||||
4. **Check Namespace** - Ensure namespace syntax is correct (`@namespace/component`)
|
||||
|
||||
### Installation Failures
|
||||
|
||||
If components fail to install:
|
||||
|
||||
1. **Check Project Setup** - Ensure you have a valid `components.json` file
|
||||
2. **Verify Paths** - Confirm the target directories exist
|
||||
3. **Check Permissions** - Ensure write permissions for component directories
|
||||
4. **Review Dependencies** - Check that required dependencies are installed
|
||||
|
||||
---
|
||||
|
||||
## Learn More
|
||||
|
||||
- [Registry Documentation](/docs/registry) - Complete guide to shadcn registries
|
||||
- [Namespaces](/docs/registry/namespace) - Configure multiple registry sources
|
||||
- [Authentication](/docs/registry/authentication) - Secure your private registries
|
||||
- [MCP Specification](https://modelcontextprotocol.io) - Learn about Model Context Protocol
|
||||
@@ -41,7 +41,7 @@ npx shadcn@latest add calendar
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install react-day-picker@8.10.1 date-fns
|
||||
npm install react-day-picker date-fns
|
||||
```
|
||||
|
||||
<Step>Add the `Button` component to your project.</Step>
|
||||
|
||||
@@ -4,9 +4,15 @@ description: Beautiful charts. Built using Recharts. Copy and paste into your ap
|
||||
component: true
|
||||
---
|
||||
|
||||
<Callout>
|
||||
|
||||
**Note:** We're working on upgrading to Recharts v3. In the meantime, if you'd like to start testing v3, see the code in the comment [here](https://github.com/shadcn-ui/ui/issues/7669#issuecomment-2998299159). We'll have an official release soon.
|
||||
|
||||
</Callout>
|
||||
|
||||
<ComponentPreview
|
||||
name="chart-bar-interactive"
|
||||
className="theme-blue -mt-4 [&_.preview]:p-0 [&_.preview]:lg:min-h-[404px] [&_.preview>div]:w-full [&_.preview>div]:border-none [&_.preview>div]:shadow-none"
|
||||
className="theme-blue [&_.preview]:h-auto [&_.preview]:p-0 [&_.preview]:lg:min-h-[404px] [&_.preview>div]:w-full [&_.preview>div]:border-none [&_.preview>div]:shadow-none"
|
||||
hideCode
|
||||
/>
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ export function DataTable<TData, TValue>({
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="rounded-md border">
|
||||
<div className="overflow-hidden rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
@@ -425,7 +425,7 @@ export function DataTable<TData, TValue>({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="rounded-md border">
|
||||
<div className="overflow-hidden rounded-md border">
|
||||
<Table>
|
||||
{ // .... }
|
||||
</Table>
|
||||
@@ -499,7 +499,7 @@ export function DataTable<TData, TValue>({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="rounded-md border">
|
||||
<div className="overflow-hidden rounded-md border">
|
||||
<Table>{ ... }</Table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -602,7 +602,7 @@ export function DataTable<TData, TValue>({
|
||||
className="max-w-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="rounded-md border">
|
||||
<div className="overflow-hidden rounded-md border">
|
||||
<Table>{ ... }</Table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -715,7 +715,7 @@ export function DataTable<TData, TValue>({
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div className="rounded-md border">
|
||||
<div className="overflow-hidden rounded-md border">
|
||||
<Table>{ ... }</Table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -805,7 +805,7 @@ export function DataTable<TData, TValue>({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="rounded-md border">
|
||||
<div className="overflow-hidden rounded-md border">
|
||||
<Table />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
397
apps/v4/content/docs/registry/authentication.mdx
Normal file
397
apps/v4/content/docs/registry/authentication.mdx
Normal file
@@ -0,0 +1,397 @@
|
||||
---
|
||||
title: Authentication
|
||||
description: Secure your registry with authentication for private and personalized components.
|
||||
---
|
||||
|
||||
Authentication lets you run private registries, control who can access your components, and give different teams or users different content. This guide shows common authentication patterns and how to set them up.
|
||||
|
||||
Authentication enables these use cases:
|
||||
|
||||
- **Private Components**: Keep your business logic and internal components secure
|
||||
- **Team-Specific Resources**: Give different teams different components
|
||||
- **Access Control**: Limit who can see sensitive or experimental components
|
||||
- **Usage Analytics**: See who's using which components in your organization
|
||||
- **Licensing**: Control who gets premium or licensed components
|
||||
|
||||
## Common Authentication Patterns
|
||||
|
||||
### Token-Based Authentication
|
||||
|
||||
The most common approach uses Bearer tokens or API keys:
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"registries": {
|
||||
"@private": {
|
||||
"url": "https://registry.company.com/{name}.json",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${REGISTRY_TOKEN}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Set your token in environment variables:
|
||||
|
||||
```bash title=".env.local"
|
||||
REGISTRY_TOKEN=your_secret_token_here
|
||||
```
|
||||
|
||||
### API Key Authentication
|
||||
|
||||
Some registries use API keys in headers:
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"registries": {
|
||||
"@company": {
|
||||
"url": "https://api.company.com/registry/{name}.json",
|
||||
"headers": {
|
||||
"X-API-Key": "${API_KEY}",
|
||||
"X-Workspace-Id": "${WORKSPACE_ID}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Query Parameter Authentication
|
||||
|
||||
For simpler setups, use query parameters:
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"registries": {
|
||||
"@internal": {
|
||||
"url": "https://registry.company.com/{name}.json",
|
||||
"params": {
|
||||
"token": "${ACCESS_TOKEN}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This creates: `https://registry.company.com/button.json?token=your_token`
|
||||
|
||||
## Server-Side Implementation
|
||||
|
||||
Here's how to add authentication to your registry server:
|
||||
|
||||
### Next.js API Route Example
|
||||
|
||||
```typescript title="app/api/registry/[name]/route.ts"
|
||||
import { NextRequest, NextResponse } from "next/server"
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { name: string } }
|
||||
) {
|
||||
// Get token from Authorization header.
|
||||
const authHeader = request.headers.get("authorization")
|
||||
const token = authHeader?.replace("Bearer ", "")
|
||||
|
||||
// Or from query parameters.
|
||||
const queryToken = request.nextUrl.searchParams.get("token")
|
||||
|
||||
// Check if token is valid.
|
||||
if (!isValidToken(token || queryToken)) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
|
||||
}
|
||||
|
||||
// Check if token can access this component.
|
||||
if (!hasAccessToComponent(token, params.name)) {
|
||||
return NextResponse.json({ error: "Forbidden" }, { status: 403 })
|
||||
}
|
||||
|
||||
// Return the component.
|
||||
const component = await getComponent(params.name)
|
||||
return NextResponse.json(component)
|
||||
}
|
||||
|
||||
function isValidToken(token: string | null) {
|
||||
// Add your token validation logic here.
|
||||
// Check against database, JWT validation, etc.
|
||||
return token === process.env.VALID_TOKEN
|
||||
}
|
||||
|
||||
function hasAccessToComponent(token: string, componentName: string) {
|
||||
// Add role-based access control here.
|
||||
// Check if token can access specific component.
|
||||
return true // Your logic here.
|
||||
}
|
||||
```
|
||||
|
||||
### Express.js Example
|
||||
|
||||
```javascript title="server.js"
|
||||
app.get("/registry/:name.json", (req, res) => {
|
||||
const token = req.headers.authorization?.replace("Bearer ", "")
|
||||
|
||||
if (!isValidToken(token)) {
|
||||
return res.status(401).json({ error: "Unauthorized" })
|
||||
}
|
||||
|
||||
const component = getComponent(req.params.name)
|
||||
if (!component) {
|
||||
return res.status(404).json({ error: "Component not found" })
|
||||
}
|
||||
|
||||
res.json(component)
|
||||
})
|
||||
```
|
||||
|
||||
## Advanced Authentication Patterns
|
||||
|
||||
### Team-Based Access
|
||||
|
||||
Give different teams different components:
|
||||
|
||||
```typescript title="api/registry/route.ts"
|
||||
async function GET(request: NextRequest) {
|
||||
const token = extractToken(request)
|
||||
const team = await getTeamFromToken(token)
|
||||
|
||||
// Get components for this team.
|
||||
const components = await getComponentsForTeam(team)
|
||||
return NextResponse.json(components)
|
||||
}
|
||||
```
|
||||
|
||||
### User-Personalized Registries
|
||||
|
||||
Give users components based on their preferences:
|
||||
|
||||
```typescript
|
||||
async function GET(request: NextRequest) {
|
||||
const user = await authenticateUser(request)
|
||||
|
||||
// Get user's style and framework preferences.
|
||||
const preferences = await getUserPreferences(user.id)
|
||||
|
||||
// Get personalized component version.
|
||||
const component = await getPersonalizedComponent(params.name, preferences)
|
||||
|
||||
return NextResponse.json(component)
|
||||
}
|
||||
```
|
||||
|
||||
### Temporary Access Tokens
|
||||
|
||||
Use expiring tokens for better security:
|
||||
|
||||
```typescript
|
||||
interface TemporaryToken {
|
||||
token: string
|
||||
expiresAt: Date
|
||||
scope: string[]
|
||||
}
|
||||
|
||||
async function validateTemporaryToken(token: string) {
|
||||
const tokenData = await getTokenData(token)
|
||||
|
||||
if (!tokenData) return false
|
||||
if (new Date() > tokenData.expiresAt) return false
|
||||
|
||||
return true
|
||||
}
|
||||
```
|
||||
|
||||
## Multi-Registry Authentication
|
||||
|
||||
With [namespaced registries](/docs/registry/namespace), you can set up multiple registries with different authentication:
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"registries": {
|
||||
"@public": "https://public.company.com/{name}.json",
|
||||
"@internal": {
|
||||
"url": "https://internal.company.com/{name}.json",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${INTERNAL_TOKEN}"
|
||||
}
|
||||
},
|
||||
"@premium": {
|
||||
"url": "https://premium.company.com/{name}.json",
|
||||
"headers": {
|
||||
"X-License-Key": "${LICENSE_KEY}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This lets you:
|
||||
|
||||
- Mix public and private registries
|
||||
- Use different authentication per registry
|
||||
- Organize components by access level
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
### Use Environment Variables
|
||||
|
||||
Never commit tokens to version control. Always use environment variables:
|
||||
|
||||
```bash title=".env.local"
|
||||
REGISTRY_TOKEN=your_secret_token_here
|
||||
API_KEY=your_api_key_here
|
||||
```
|
||||
|
||||
Then reference them in `components.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"registries": {
|
||||
"@private": {
|
||||
"url": "https://registry.company.com/{name}.json",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${REGISTRY_TOKEN}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Use HTTPS
|
||||
|
||||
Always use HTTPS URLs for registries to protect your tokens in transit:
|
||||
|
||||
```json
|
||||
{
|
||||
"@secure": "https://registry.company.com/{name}.json" // ✅
|
||||
"@insecure": "http://registry.company.com/{name}.json" // ❌
|
||||
}
|
||||
```
|
||||
|
||||
### Add Rate Limiting
|
||||
|
||||
Protect your registry from abuse:
|
||||
|
||||
```typescript
|
||||
import rateLimit from "express-rate-limit"
|
||||
|
||||
const limiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 100, // limit each IP to 100 requests per windowMs
|
||||
})
|
||||
|
||||
app.use("/registry", limiter)
|
||||
```
|
||||
|
||||
### Rotate Tokens
|
||||
|
||||
Change access tokens regularly:
|
||||
|
||||
```typescript
|
||||
// Create new token with expiration.
|
||||
function generateToken() {
|
||||
const token = crypto.randomBytes(32).toString("hex")
|
||||
const expiresAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) // 30 days.
|
||||
|
||||
return { token, expiresAt }
|
||||
}
|
||||
```
|
||||
|
||||
### Log Access
|
||||
|
||||
Track registry access for security and analytics:
|
||||
|
||||
```typescript
|
||||
async function logAccess(request: Request, component: string, userId: string) {
|
||||
await db.accessLog.create({
|
||||
timestamp: new Date(),
|
||||
userId,
|
||||
component,
|
||||
ip: request.ip,
|
||||
userAgent: request.headers["user-agent"],
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Authentication
|
||||
|
||||
Test your authenticated registry locally:
|
||||
|
||||
```bash
|
||||
# Test with curl.
|
||||
curl -H "Authorization: Bearer your_token" \
|
||||
https://registry.company.com/button.json
|
||||
|
||||
# Test with the CLI.
|
||||
REGISTRY_TOKEN=your_token npx shadcn@latest add @private/button
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
The shadcn CLI handles authentication errors gracefully:
|
||||
|
||||
- **401 Unauthorized**: Token is invalid or missing
|
||||
- **403 Forbidden**: Token lacks permission for this resource
|
||||
- **429 Too Many Requests**: Rate limit exceeded
|
||||
|
||||
### Custom Error Messages
|
||||
|
||||
Your registry server can return custom error messages in the response body, and the CLI will display them to users:
|
||||
|
||||
```typescript
|
||||
// Registry server returns custom error
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "Unauthorized",
|
||||
message: "Your subscription has expired. Please renew at company.com/billing"
|
||||
},
|
||||
{ status: 403 }
|
||||
)
|
||||
```
|
||||
|
||||
The user will see:
|
||||
|
||||
```txt
|
||||
Your subscription has expired. Please renew at company.com/billing
|
||||
```
|
||||
|
||||
This helps provide context-specific guidance:
|
||||
|
||||
```typescript
|
||||
// Different error messages for different scenarios
|
||||
if (!token) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "Unauthorized",
|
||||
message: "Authentication required. Set REGISTRY_TOKEN in your .env.local file"
|
||||
},
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
if (isExpiredToken(token)) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "Unauthorized",
|
||||
message: "Token expired. Request a new token at company.com/tokens"
|
||||
},
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
if (!hasTeamAccess(token, component)) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "Forbidden",
|
||||
message: `Component '${component}' is restricted to the Design team`
|
||||
},
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
To set up authentication with multiple registries and advanced patterns, see the [Namespaced Registries](/docs/registry/namespace) documentation. It covers:
|
||||
|
||||
- Setting up multiple authenticated registries
|
||||
- Using different authentication per namespace
|
||||
- Cross-registry dependency resolution
|
||||
- Advanced authentication patterns
|
||||
@@ -328,6 +328,20 @@ Add custom theme variables to the `theme` object.
|
||||
}
|
||||
```
|
||||
|
||||
## Add custom plugins
|
||||
|
||||
```json title="example-plugin.json" showLineNumbers
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
||||
"name": "custom-plugin",
|
||||
"type": "registry:component",
|
||||
"css": {
|
||||
"@plugin @tailwindcss/typography": {},
|
||||
"@plugin foo": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Add custom animations
|
||||
|
||||
Note: you need to define both `@keyframes` in css and `theme` in cssVars to use animations.
|
||||
@@ -354,3 +368,95 @@ Note: you need to define both `@keyframes` in css and `theme` in cssVars to use
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Add environment variables
|
||||
|
||||
You can add environment variables using the `envVars` field.
|
||||
|
||||
```json title="example-item.json" showLineNumbers {5-9}
|
||||
{»
|
||||
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
||||
"name": "custom-item",
|
||||
"type": "registry:item",
|
||||
"envVars": {
|
||||
"NEXT_PUBLIC_APP_URL": "http://localhost:4000",
|
||||
"DATABASE_URL": "postgresql://postgres:postgres@localhost:5432/postgres",
|
||||
"OPENAI_API_KEY": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Environment variables are added to the `.env.local` or `.env` file. Existing variables are not overwritten.
|
||||
|
||||
<Callout>
|
||||
|
||||
**IMPORTANT:** Use `envVars` to add development or example variables. Do NOT use it to add production variables.
|
||||
|
||||
</Callout>
|
||||
|
||||
## Universal Items
|
||||
|
||||
As of `2.9.0`, you can create universal items that can be installed without framework detection or components.json.
|
||||
|
||||
To make an item universal i.e framework agnostic, all the files in the item must have an explicit target.
|
||||
|
||||
Here's an example of a registry item that installs custom Cursor rules for _python_:
|
||||
|
||||
```json title=".cursor/rules/custom-python.mdc" showLineNumbers {9}
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
||||
"name": "python-rules",
|
||||
"type": "registry:item",
|
||||
"files": [
|
||||
{
|
||||
"path": "/path/to/your/registry/default/custom-python.mdc",
|
||||
"type": "registry:file",
|
||||
"target": "~/.cursor/rules/custom-python.mdc",
|
||||
"content": "..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Here's another example for installation custom ESLint config:
|
||||
|
||||
```json title=".eslintrc.json" showLineNumbers {9}
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
||||
"name": "my-eslint-config",
|
||||
"type": "registry:item",
|
||||
"files": [
|
||||
{
|
||||
"path": "/path/to/your/registry/default/custom-eslint.json",
|
||||
"type": "registry:file",
|
||||
"target": "~/.eslintrc.json",
|
||||
"content": "..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
You can also have a universal item that installs multiple files:
|
||||
|
||||
```json title="my-custom-starter-template.json" showLineNumbers {9}
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
||||
"name": "my-custom-start-template",
|
||||
"type": "registry:item",
|
||||
dependencies: ["better-auth"]
|
||||
"files": [
|
||||
{
|
||||
"path": "/path/to/file-01.json",
|
||||
"type": "registry:file",
|
||||
"target": "~/file-01.json",
|
||||
"content": "..."
|
||||
},
|
||||
{
|
||||
"path": "/path/to/file-02.vue",
|
||||
"type": "registry:file",
|
||||
"target": "~/pages/file-02.vue",
|
||||
"content": "..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -3,23 +3,27 @@ title: Getting Started
|
||||
description: Learn how to get setup and run your own component registry.
|
||||
---
|
||||
|
||||
This guide will walk you through the process of setting up your own component registry.
|
||||
|
||||
It assumes you already have a project with components and would like to turn it into a registry.
|
||||
This guide will walk you through the process of setting up your own component registry. It assumes you already have a project with components and would like to turn it into a registry.
|
||||
|
||||
If you're starting a new registry project, you can use the [registry template](https://github.com/shadcn-ui/registry-template) as a starting point. We have already configured it for you.
|
||||
|
||||
## Requirements
|
||||
|
||||
You are free to design and host your custom registry as you see fit. The only requirement is that your registry items must be valid JSON files that conform to the [registry-item schema specification](/docs/registry/registry-item-json).
|
||||
|
||||
If you'd like to see an example of a registry, we have a [template project](https://github.com/shadcn-ui/registry-template) for you to use as a starting point.
|
||||
|
||||
## registry.json
|
||||
|
||||
The `registry.json` file is only required if you're using the `shadcn` CLI to build your registry.
|
||||
The `registry.json` is the entry point for the registry. It contains the registry's name, homepage, and defines all the items present in the registry.
|
||||
|
||||
If you're using a different build system, you can skip this step as long as your build system produces valid JSON files that conform to the [registry-item schema specification](/docs/registry/registry-item-json).
|
||||
Your registry must have this file (or JSON payload) present at the root of the registry endpoint. The registry endpoint is the URL where your registry is hosted.
|
||||
|
||||
<Steps>
|
||||
The `shadcn` CLI will automatically generate this file for you when you run the `build` command.
|
||||
|
||||
### Add a registry.json file
|
||||
## Add a registry.json file
|
||||
|
||||
Create a `registry.json` file in the root of your project. Your project can be a Next.js, Remix, Vite, or any other project that supports React.
|
||||
Create a `registry.json` file in the root of your project. Your project can be a Next.js, Vite, Vue, Svelte, PHP or any other framework as long as it supports serving JSON over HTTP.
|
||||
|
||||
```json title="registry.json" showLineNumbers
|
||||
{
|
||||
@@ -34,12 +38,8 @@ Create a `registry.json` file in the root of your project. Your project can be a
|
||||
|
||||
This `registry.json` file must conform to the [registry schema specification](/docs/registry/registry-json).
|
||||
|
||||
</Steps>
|
||||
|
||||
## Add a registry item
|
||||
|
||||
<Steps>
|
||||
|
||||
### Create your component
|
||||
|
||||
Add your first component. Here's an example of a simple `<HelloWorld />` component:
|
||||
@@ -98,16 +98,10 @@ For every file you add, you must specify the `path` and `type` of the file. The
|
||||
|
||||
You can read more about the registry item schema and file types in the [registry item schema docs](/docs/registry/registry-item-json).
|
||||
|
||||
</Steps>
|
||||
|
||||
## Build your registry
|
||||
|
||||
<Steps>
|
||||
|
||||
### Install the shadcn CLI
|
||||
|
||||
Note: the `build` command is currently only available in the `shadcn@canary` version of the CLI.
|
||||
|
||||
```bash
|
||||
npm install shadcn@canary
|
||||
```
|
||||
@@ -140,8 +134,6 @@ You can change the output directory by passing the `--output` option. See the [s
|
||||
|
||||
</Callout>
|
||||
|
||||
</Steps>
|
||||
|
||||
## Serve your registry
|
||||
|
||||
If you're running your registry on Next.js, you can now serve your registry by running the `next` server. The command might differ for other frameworks.
|
||||
@@ -156,24 +148,13 @@ Your files will now be served at `http://localhost:3000/r/[NAME].json` eg. `http
|
||||
|
||||
To make your registry available to other developers, you can publish it by deploying your project to a public URL.
|
||||
|
||||
## Adding Auth
|
||||
|
||||
The `shadcn` CLI does not offer a built-in way to add auth to your registry. We recommend handling authorization on your registry server.
|
||||
|
||||
A common simple approach is to use a `token` query parameter to authenticate requests to your registry. e.g. `http://localhost:3000/r/hello-world.json?token=[SECURE_TOKEN_HERE]`.
|
||||
|
||||
Use the secure token to authenticate requests and return a 401 Unauthorized response if the token is invalid. Both the `shadcn` CLI and `Open in v0` will handle the 401 response and display a message to the user.
|
||||
|
||||
<Callout className="mt-6">
|
||||
**Note:** Make sure to encrypt and expire tokens.
|
||||
</Callout>
|
||||
|
||||
## Guidelines
|
||||
|
||||
Here are some guidelines to follow when building components for a registry.
|
||||
|
||||
- Place your registry item in the `registry/[STYLE]/[NAME]` directory. I'm using `new-york` as an example. It can be anything you want as long as it's nested under the `registry` directory.
|
||||
- The following properties are required for the block definition: `name`, `description`, `type` and `files`.
|
||||
- It is recommended to add a proper name and description to your registry item. This helps LLMs understand the component and its purpose.
|
||||
- Make sure to list all registry dependencies in `registryDependencies`. A registry dependency is the name of the component in the registry eg. `input`, `button`, `card`, etc or a URL to a registry item eg. `http://localhost:3000/r/editor.json`.
|
||||
- Make sure to list all dependencies in `dependencies`. A dependency is the name of the package in the registry eg. `zod`, `sonner`, etc. To set a version, you can use the `name@version` format eg. `zod@^3.20.0`.
|
||||
- **Imports should always use the `@/registry` path.** eg. `import { HelloWorld } from "@/registry/new-york/hello-world/hello-world"`
|
||||
@@ -186,3 +167,7 @@ To install a registry item using the `shadcn` CLI, use the `add` command followe
|
||||
```bash
|
||||
npx shadcn@latest add http://localhost:3000/r/hello-world.json
|
||||
```
|
||||
|
||||
See the [Namespaced
|
||||
Registries](/docs/registry/namespaced-registries) docs for more information on
|
||||
how to install registry items from a namespaced registry.
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
---
|
||||
title: Registry
|
||||
description: Run your own component registry.
|
||||
title: Introduction
|
||||
description: Run your own code registry.
|
||||
---
|
||||
|
||||
You can use the `shadcn` CLI to run your own code registry. Running your own registry allows you to distribute your custom components, hooks, pages, config, rules and other files to any project.
|
||||
|
||||
<Callout>
|
||||
**Note:** This feature is currently experimental. Help us improve it by
|
||||
testing it out and sending feedback. If you have any questions, please [reach
|
||||
out to us](https://github.com/shadcn-ui/ui/discussions).
|
||||
**Note:** The registry works with any project type and any framework, and is
|
||||
not limited to React.
|
||||
</Callout>
|
||||
|
||||
You can use the `shadcn` CLI to run your own component registry. Running your own registry allows you to distribute your custom components, hooks, pages, and other files to any React project.
|
||||
|
||||
<figure className="flex flex-col gap-4">
|
||||
<Image
|
||||
src="/images/registry-light.png"
|
||||
@@ -27,14 +26,54 @@ You can use the `shadcn` CLI to run your own component registry. Running your ow
|
||||
className="mt-6 hidden w-full overflow-hidden rounded-lg border shadow-sm dark:block"
|
||||
/>
|
||||
<figcaption className="text-center text-sm text-gray-500">
|
||||
Distribute code to any React project.
|
||||
A distribution system for code
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
Registry items are automatically compatible with the `shadcn` CLI and `Open in v0`.
|
||||
Ready to create your own registry? In the next section, we'll walk you through setting up your own custom registry step-by-step, from creating your first component to publishing it for others to use.
|
||||
|
||||
## Requirements
|
||||
<div className="mt-6 grid gap-4 sm:grid-cols-2">
|
||||
<LinkedCard href="/docs/registry/getting-started" className="items-start text-sm md:p-6">
|
||||
<div className="font-medium">Getting Started</div>
|
||||
<div className="text-muted-foreground">
|
||||
Set up and build your own registry
|
||||
</div>
|
||||
</LinkedCard>
|
||||
|
||||
You are free to design and host your custom registry as you see fit. The only requirement is that your registry items must be valid JSON files that conform to the [registry-item schema specification](/docs/registry/registry-item-json).
|
||||
|
||||
If you'd like to see an example of a registry, we have a [template project](https://github.com/shadcn-ui/registry-template) for you to use as a starting point.
|
||||
<LinkedCard
|
||||
href="/docs/registry/authentication"
|
||||
className="items-start text-sm md:p-6"
|
||||
>
|
||||
<div className="font-medium">Authentication</div>
|
||||
<div className="text-muted-foreground">
|
||||
Secure your registry with authentication
|
||||
</div>
|
||||
</LinkedCard>
|
||||
<LinkedCard
|
||||
href="/docs/registry/namespace"
|
||||
className="items-start text-sm md:p-6"
|
||||
>
|
||||
<div className="font-medium">Namespaces</div>
|
||||
<div className="text-muted-foreground">
|
||||
Configure registries with namespaces
|
||||
</div>
|
||||
</LinkedCard>
|
||||
<LinkedCard
|
||||
href="/docs/registry/examples"
|
||||
className="items-start text-sm md:p-6"
|
||||
>
|
||||
<div className="font-medium">Examples</div>
|
||||
<div className="text-muted-foreground">
|
||||
Registry item examples and configurations
|
||||
</div>
|
||||
</LinkedCard>
|
||||
<LinkedCard
|
||||
href="/docs/registry/registry-json"
|
||||
className="items-start text-sm md:p-6"
|
||||
>
|
||||
<div className="font-medium">Schema</div>
|
||||
<div className="text-muted-foreground">
|
||||
Schema specification for registry.json
|
||||
</div>
|
||||
</LinkedCard>
|
||||
</div>
|
||||
|
||||
102
apps/v4/content/docs/registry/mcp.mdx
Normal file
102
apps/v4/content/docs/registry/mcp.mdx
Normal file
@@ -0,0 +1,102 @@
|
||||
---
|
||||
title: MCP Server
|
||||
description: MCP support for registry developers
|
||||
---
|
||||
|
||||
The [shadcn MCP server](/docs/mcp) works out of the box with any shadcn-compatible registry. You do not need to do anything special to enable MCP support for your registry.
|
||||
|
||||
See the [MCP documentation](/docs/mcp) for more information on how to use the shadcn MCP server.
|
||||
|
||||
---
|
||||
|
||||
## Configuring MCP
|
||||
|
||||
Ask your registry consumers to configure your registry in their `components.json` file and install the shadcn MCP server:
|
||||
|
||||
<Tabs defaultValue="claude">
|
||||
<TabsList>
|
||||
<TabsTrigger value="claude">Claude Code</TabsTrigger>
|
||||
<TabsTrigger value="cursor">Cursor</TabsTrigger>
|
||||
<TabsTrigger value="vscode">VS Code</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="claude" className="mt-4">
|
||||
**Configure your registry** in your `components.json` file:
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"registries": {
|
||||
"@acme": "https://acme.com/r/{name}.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Run the following command** in your project:
|
||||
|
||||
```bash
|
||||
npx shadcn@latest mcp init --client claude
|
||||
```
|
||||
|
||||
**Restart Claude Code** and try the following prompts:
|
||||
- Show me all available components in the shadcn registry
|
||||
- Add the button, dialog and card components to my project
|
||||
- Create a contact form using components from the shadcn registry
|
||||
|
||||
**Note:** You can use `/mcp` command in Claude Code to debug the MCP server.
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="cursor" className="mt-4">
|
||||
**Configure your registry** in your `components.json` file:
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"registries": {
|
||||
"@acme": "https://acme.com/r/{name}.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
**Run the following command** in your project:
|
||||
```bash
|
||||
npx shadcn@latest mcp init --client cursor
|
||||
```
|
||||
|
||||
Open **Cursor Settings** and **Enable the MCP server** for shadcn. Then try the following prompts:
|
||||
- Show me all available components in the shadcn registry
|
||||
- Add the button, dialog and card components to my project
|
||||
- Create a contact form using components from the shadcn registry
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="vscode" className="mt-4">
|
||||
**Configure your registry** in your `components.json` file:
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"registries": {
|
||||
"@acme": "https://acme.com/r/{name}.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
**Run the following command** in your project:
|
||||
```bash
|
||||
npx shadcn@latest mcp init --client vscode
|
||||
```
|
||||
|
||||
Open `.vscode/mcp.json` and click **Start** next to the shadcn server. Then try the following prompts with GitHub Copilot:
|
||||
- Show me all available components in the shadcn registry
|
||||
- Add the button, dialog and card components to my project
|
||||
- Create a contact form using components from the shadcn registry
|
||||
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
### Best Practices
|
||||
|
||||
Here are some best practices for MCP-compatible registries:
|
||||
|
||||
1. **Clear Descriptions**: Add concise, informative descriptions that help AI assistants understand what a registry item is for and how to use it.
|
||||
2. **Proper Dependencies**: List all `dependencies` accurately so MCP can install them automatically.
|
||||
3. **Registry Dependencies**: Use `registryDependencies` to indicate relationships between items.
|
||||
4. **Consistent Naming**: Use kebab-case for component names and maintain consistency across your registry.
|
||||
@@ -1,10 +1,13 @@
|
||||
{
|
||||
"title": "Registry",
|
||||
"pages": [
|
||||
"index",
|
||||
"getting-started",
|
||||
"faq",
|
||||
"open-in-v0",
|
||||
"namespace",
|
||||
"authentication",
|
||||
"examples",
|
||||
"mcp",
|
||||
"open-in-v0",
|
||||
"registry-json",
|
||||
"registry-item-json"
|
||||
]
|
||||
|
||||
957
apps/v4/content/docs/registry/namespace.mdx
Normal file
957
apps/v4/content/docs/registry/namespace.mdx
Normal file
@@ -0,0 +1,957 @@
|
||||
---
|
||||
title: Namespaces
|
||||
description: Configure and use multiple resource registries with namespace support.
|
||||
---
|
||||
|
||||
Namespaced registries let you configure multiple resource sources in one project. This means you can install components, libraries, utilities, AI prompts, configuration files, and other resources from various registries, whether they're public, third-party, or your own custom private libraries.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Decentralized Namespace System](#decentralized-namespace-system)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Registry Naming Convention](#registry-naming-convention)
|
||||
- [Configuration](#configuration)
|
||||
- [Authentication & Security](#authentication--security)
|
||||
- [Versioning](#versioning)
|
||||
- [Dependency Resolution](#dependency-resolution)
|
||||
- [Built-in Registries](#built-in-registries)
|
||||
- [CLI Commands](#cli-commands)
|
||||
- [Error Handling](#error-handling)
|
||||
- [Creating Your Own Registry](#creating-your-own-registry)
|
||||
- [Example Configurations](#example-configurations)
|
||||
- [Technical Details](#technical-details)
|
||||
- [Best Practices](#best-practices)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Overview
|
||||
|
||||
Registry namespaces are prefixed with `@` and provide a way to organize and reference resources from different sources. Resources can be any type of content: components, libraries, utilities, hooks, AI prompts, configuration files, themes, and more. For example:
|
||||
|
||||
- `@shadcn/button` - UI component from the shadcn registry
|
||||
- `@v0/dashboard` - Dashboard component from the v0 registry
|
||||
- `@ai-elements/input` - AI prompt input from an AI elements registry
|
||||
- `@acme/auth-utils` - Authentication utilities from your company's private registry
|
||||
- `@ai/chatbot-rules` - AI prompt rules from an AI resources registry
|
||||
- `@themes/dark-mode` - Theme configuration from a themes registry
|
||||
|
||||
## Decentralized Namespace System
|
||||
|
||||
We intentionally designed the namespace system to be decentralized. There is no central registrar for namespaces. You are free to create and use any namespace you want.
|
||||
|
||||
This decentralized approach gives you complete flexibility to organize your resources however makes sense for your organization.
|
||||
|
||||
You can create multiple registries for different purposes:
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"registries": {
|
||||
"@acme-ui": "https://registry.acme.com/ui/{name}.json",
|
||||
"@acme-docs": "https://registry.acme.com/docs/{name}.json",
|
||||
"@acme-ai": "https://registry.acme.com/ai/{name}.json",
|
||||
"@acme-themes": "https://registry.acme.com/themes/{name}.json",
|
||||
"@acme-internal": {
|
||||
"url": "https://internal.acme.com/registry/{name}.json",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${INTERNAL_TOKEN}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This allows you to:
|
||||
|
||||
- **Organize by type**: Separate UI components, documentation, AI resources, etc.
|
||||
- **Organize by team**: Different teams can maintain their own registries
|
||||
- **Organize by visibility**: Public vs. private resources
|
||||
- **Organize by version**: Stable vs. experimental registries
|
||||
- **No naming conflicts**: Since there's no central authority, you don't need to worry about namespace collisions
|
||||
|
||||
### Examples of Multi-Registry Setups
|
||||
|
||||
#### By Resource Type
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"@components": "https://cdn.company.com/components/{name}.json",
|
||||
"@hooks": "https://cdn.company.com/hooks/{name}.json",
|
||||
"@utils": "https://cdn.company.com/utils/{name}.json",
|
||||
"@prompts": "https://cdn.company.com/ai-prompts/{name}.json"
|
||||
}
|
||||
```
|
||||
|
||||
#### By Team or Department
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"@design": "https://design.company.com/registry/{name}.json",
|
||||
"@engineering": "https://eng.company.com/registry/{name}.json",
|
||||
"@marketing": "https://marketing.company.com/registry/{name}.json"
|
||||
}
|
||||
```
|
||||
|
||||
#### By Stability
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"@stable": "https://registry.company.com/stable/{name}.json",
|
||||
"@latest": "https://registry.company.com/beta/{name}.json",
|
||||
"@experimental": "https://registry.company.com/experimental/{name}.json"
|
||||
}
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Installing Resources
|
||||
|
||||
Once configured, you can install resources using the namespace syntax:
|
||||
|
||||
```bash
|
||||
npx shadcn@beta add @v0/dashboard
|
||||
```
|
||||
|
||||
or multiple resources at once:
|
||||
|
||||
```bash
|
||||
npx shadcn@beta add @acme/header @lib/auth-utils @ai/chatbot-rules
|
||||
```
|
||||
|
||||
### Quick Configuration
|
||||
|
||||
Add registries to your `components.json`:
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"registries": {
|
||||
"@v0": "https://v0.dev/chat/b/{name}",
|
||||
"@acme": "https://registry.acme.com/resources/{name}.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then start installing:
|
||||
|
||||
```bash
|
||||
npx shadcn@beta add @acme/button
|
||||
```
|
||||
|
||||
## Registry Naming Convention
|
||||
|
||||
Registry names must follow these rules:
|
||||
|
||||
- Start with `@` symbol
|
||||
- Contain only alphanumeric characters, hyphens, and underscores
|
||||
- Examples of valid names: `@v0`, `@acme-ui`, `@my_company`
|
||||
|
||||
The pattern for referencing resources is: `@namespace/resource-name`
|
||||
|
||||
## Configuration
|
||||
|
||||
Namespaced registries are configured in your `components.json` file under the `registries` field.
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
The simplest way to configure a registry is with a URL template string:
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"registries": {
|
||||
"@v0": "https://v0.dev/chat/b/{name}",
|
||||
"@acme": "https://registry.acme.com/resources/{name}.json",
|
||||
"@lib": "https://lib.company.com/utilities/{name}",
|
||||
"@ai": "https://ai-resources.com/r/{name}.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **Note:** The `{name}` placeholder in the URL is automatically parsed and replaced with the resource name when you run `npx shadcn@beta add @namespace/resource-name`. For example, `@acme/button` becomes `https://registry.acme.com/resources/button.json`. See [URL Pattern System](#url-pattern-system) for more details.
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
For registries that require authentication or additional parameters, use the object format:
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"registries": {
|
||||
"@private": {
|
||||
"url": "https://api.company.com/registry/{name}.json",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${REGISTRY_TOKEN}",
|
||||
"X-API-Key": "${API_KEY}"
|
||||
},
|
||||
"params": {
|
||||
"version": "latest",
|
||||
"format": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **Note:** Environment variables in the format `${VAR_NAME}` are automatically expanded from your environment (process.env). This works in URLs, headers, and params. For example, `${REGISTRY_TOKEN}` will be replaced with the value of `process.env.REGISTRY_TOKEN`. See [Authentication & Security](#authentication--security) for more details on using environment variables.
|
||||
|
||||
### URL Pattern System
|
||||
|
||||
Registry URLs support the following placeholders:
|
||||
|
||||
### `{name}` Placeholder (required)
|
||||
|
||||
The `{name}` placeholder is replaced with the resource name:
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"@acme": "https://registry.acme.com/{name}.json"
|
||||
}
|
||||
```
|
||||
|
||||
When installing `@acme/button`, the URL becomes: `https://registry.acme.com/button.json`
|
||||
When installing `@acme/auth-utils`, the URL becomes: `https://registry.acme.com/auth-utils.json`
|
||||
|
||||
### `{style}` Placeholder (optional)
|
||||
|
||||
The `{style}` placeholder is replaced with the current style configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"@themes": "https://registry.example.com/{style}/{name}.json"
|
||||
}
|
||||
```
|
||||
|
||||
With style set to `new-york`, installing `@themes/card` resolves to: `https://registry.example.com/new-york/card.json`
|
||||
|
||||
The style placeholder is optional. Use this when you want to serve different versions of the same resource. For example, you can serve a different version of a component for each style.
|
||||
|
||||
## Authentication & Security
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Use environment variables to securely store credentials:
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"registries": {
|
||||
"@private": {
|
||||
"url": "https://api.company.com/registry/{name}.json",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${REGISTRY_TOKEN}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then set the environment variable:
|
||||
|
||||
```bash title=".env.local"
|
||||
REGISTRY_TOKEN=your_secret_token_here
|
||||
```
|
||||
|
||||
### Authentication Methods
|
||||
|
||||
#### Bearer Token (OAuth 2.0)
|
||||
|
||||
```json
|
||||
{
|
||||
"@github": {
|
||||
"url": "https://api.github.com/repos/org/registry/contents/{name}.json",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${GITHUB_TOKEN}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### API Key in Headers
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"@private": {
|
||||
"url": "https://api.company.com/registry/{name}",
|
||||
"headers": {
|
||||
"X-API-Key": "${API_KEY}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Basic Authentication
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"@internal": {
|
||||
"url": "https://registry.company.com/{name}.json",
|
||||
"headers": {
|
||||
"Authorization": "Basic ${BASE64_CREDENTIALS}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Query Parameter Authentication
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"@secure": {
|
||||
"url": "https://registry.example.com/{name}.json",
|
||||
"params": {
|
||||
"api_key": "${API_KEY}",
|
||||
"client_id": "${CLIENT_ID}",
|
||||
"signature": "${REQUEST_SIGNATURE}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Multiple Authentication Methods
|
||||
|
||||
Some registries require multiple authentication methods:
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"@enterprise": {
|
||||
"url": "https://api.enterprise.com/v2/registry/{name}",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${ACCESS_TOKEN}",
|
||||
"X-API-Key": "${API_KEY}",
|
||||
"X-Workspace-Id": "${WORKSPACE_ID}"
|
||||
},
|
||||
"params": {
|
||||
"version": "latest"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Security Considerations
|
||||
|
||||
When working with namespaced registries, especially third-party or public ones, security is paramount. Here's how we handle security:
|
||||
|
||||
### Resource Validation
|
||||
|
||||
All resources fetched from registries are validated against our registry item schema before installation. This ensures:
|
||||
|
||||
- **Structure validation**: Resources must conform to the expected JSON schema
|
||||
- **Type safety**: Resource types are validated (`registry:ui`, `registry:lib`, etc.)
|
||||
- **No arbitrary code execution**: Resources are data files, not executable scripts
|
||||
|
||||
### Environment Variable Security
|
||||
|
||||
Environment variables used for authentication are:
|
||||
|
||||
- **Never logged**: The CLI never logs or displays environment variable values
|
||||
- **Expanded at runtime**: Variables are only expanded when needed, not stored
|
||||
- **Isolated per registry**: Each registry maintains its own authentication context
|
||||
|
||||
Example of secure configuration:
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"registries": {
|
||||
"@private": {
|
||||
"url": "https://api.company.com/registry/{name}.json",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${PRIVATE_REGISTRY_TOKEN}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Never commit actual tokens to version control. Use `.env.local`:
|
||||
|
||||
```bash title=".env.local"
|
||||
PRIVATE_REGISTRY_TOKEN=actual_token_here
|
||||
```
|
||||
|
||||
### HTTPS Enforcement
|
||||
|
||||
We strongly recommend using HTTPS for all registry URLs:
|
||||
|
||||
- **Encrypted transport**: Prevents man-in-the-middle attacks
|
||||
- **Certificate validation**: Ensures you're connecting to the legitimate registry
|
||||
- **Credential protection**: Headers and tokens are encrypted in transit
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"registries": {
|
||||
"@secure": "https://registry.example.com/{name}.json", // ✅ Good
|
||||
"@insecure": "http://registry.example.com/{name}.json" // ❌ Avoid
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Content Security
|
||||
|
||||
Resources from registries are treated as data, not code:
|
||||
|
||||
1. **JSON parsing only**: Resources must be valid JSON
|
||||
2. **Schema validation**: Must match the registry item schema
|
||||
3. **File path restrictions**: Files can only be written to configured paths
|
||||
4. **No script execution**: The CLI doesn't execute any code from registry resources
|
||||
|
||||
### Registry Trust Model
|
||||
|
||||
The namespace system operates on a trust model:
|
||||
|
||||
- **You trust what you install**: Only add registries you trust to your configuration
|
||||
- **Explicit configuration**: Registries must be explicitly configured in `components.json`
|
||||
- **No automatic registry discovery**: The CLI never automatically adds registries
|
||||
- **Dependency transparency**: All dependencies are clearly listed in registry items
|
||||
|
||||
### Best Practices for Registry Operators
|
||||
|
||||
If you're running your own registry:
|
||||
|
||||
1. **Use HTTPS always**: Never serve registry content over HTTP
|
||||
2. **Implement authentication**: Require API keys or tokens for private registries
|
||||
3. **Rate limiting**: Protect your registry from abuse
|
||||
4. **Content validation**: Validate resources before serving them
|
||||
|
||||
Example secure registry setup:
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"@company": {
|
||||
"url": "https://registry.company.com/v1/{name}.json",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${COMPANY_TOKEN}",
|
||||
"X-Registry-Version": "1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Inspecting Resources Before Installation
|
||||
|
||||
The CLI provides transparency about what's being installed. You can see the payload of a registry item using the following command:
|
||||
|
||||
```bash
|
||||
npx shadcn@beta view @acme/button
|
||||
```
|
||||
|
||||
This will output the payload of the registry item to the console.
|
||||
|
||||
## Dependency Resolution
|
||||
|
||||
### Basic Dependency Resolution
|
||||
|
||||
Resources can have dependencies across different registries:
|
||||
|
||||
```json title="registry-item.json" showLineNumbers
|
||||
{
|
||||
"name": "dashboard",
|
||||
"type": "registry:block",
|
||||
"registryDependencies": [
|
||||
"@shadcn/card", // From default registry
|
||||
"@v0/chart", // From v0 registry
|
||||
"@acme/data-table", // From acme registry
|
||||
"@lib/data-fetcher", // Utility library
|
||||
"@ai/analytics-prompt" // AI prompt resource
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The CLI automatically resolves and installs all dependencies from their respective registries.
|
||||
|
||||
### Advanced Dependency Resolution
|
||||
|
||||
Understanding how dependencies are resolved internally is important if you're developing registries or need to customize third-party resources.
|
||||
|
||||
### How Resolution Works
|
||||
|
||||
When you run `npx shadcn@beta add @namespace/resource`, the CLI does the following:
|
||||
|
||||
1. **Clears registry context** to start fresh
|
||||
2. **Fetches the main resource** from the specified registry
|
||||
3. **Recursively resolves dependencies** from their respective registries
|
||||
4. **Applies topological sorting** to ensure proper installation order
|
||||
5. **Deduplicates files** based on target paths (last one wins)
|
||||
6. **Deep merges configurations** (tailwind, cssVars, css, envVars)
|
||||
|
||||
This means that if you run the following command:
|
||||
|
||||
```bash
|
||||
npx shadcn@beta add @acme/auth @custom/login-form
|
||||
```
|
||||
|
||||
The `login-form.ts` from `@custom/login-form` will override the `login-form.ts` from `@acme/auth` because it's resolved last.
|
||||
|
||||
### Overriding Third-Party Resources
|
||||
|
||||
You can leverage the dependency resolution process to override any third-party resource by adding them to your custom resource under `registryDependencies` and overriding with your own custom values.
|
||||
|
||||
#### Example: Customizing a Third-Party Button
|
||||
|
||||
Let's say you want to customize a button from a vendor registry:
|
||||
|
||||
**1. Original vendor button** (`@vendor/button`):
|
||||
|
||||
```json title="button.json" showLineNumbers
|
||||
{
|
||||
"name": "button",
|
||||
"type": "registry:ui",
|
||||
"files": [
|
||||
{
|
||||
"path": "components/ui/button.tsx",
|
||||
"type": "registry:ui",
|
||||
"content": "// Vendor's button implementation\nexport function Button() { ... }"
|
||||
}
|
||||
],
|
||||
"cssVars": {
|
||||
"light": {
|
||||
"--button-bg": "blue"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**2. Create your custom override** (`@my-company/custom-button`):
|
||||
|
||||
```json title="custom-button.json" showLineNumbers
|
||||
{
|
||||
"name": "custom-button",
|
||||
"type": "registry:ui",
|
||||
"registryDependencies": [
|
||||
"@vendor/button" // Import original first
|
||||
],
|
||||
"cssVars": {
|
||||
"light": {
|
||||
"--button-bg": "purple" // Override the color
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**3. Install your custom version**:
|
||||
|
||||
```bash
|
||||
npx shadcn@beta add @my-company/custom-button
|
||||
```
|
||||
|
||||
This installs the original button from `@vendor/button` and then overrides the `cssVars` with your own custom values.
|
||||
|
||||
### Advanced Override Patterns
|
||||
|
||||
#### Extending Without Replacing
|
||||
|
||||
Keep the original and add extensions:
|
||||
|
||||
```json title="extended-table.json" showLineNumbers
|
||||
{
|
||||
"name": "extended-table",
|
||||
"registryDependencies": ["@vendor/table"],
|
||||
"files": [
|
||||
{
|
||||
"path": "components/ui/table-extended.tsx",
|
||||
"content": "import { Table } from '@vendor/table'\n// Add your extensions\nexport function ExtendedTable() { ... }"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
This will install the original table from `@vendor/table` and then add your extensions to `components/ui/table-extended.tsx`.
|
||||
|
||||
#### Partial Override (Multi-file Resources)
|
||||
|
||||
Override only specific files from a complex component:
|
||||
|
||||
```json title="custom-auth.json" showLineNumbers
|
||||
{
|
||||
"name": "custom-auth",
|
||||
"registryDependencies": [
|
||||
"@vendor/auth" // Has multiple files
|
||||
],
|
||||
"files": [
|
||||
{
|
||||
"path": "lib/auth-server.ts",
|
||||
"type": "registry:lib",
|
||||
"content": "// Your custom auth server"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Resolution Order Example
|
||||
|
||||
When you install `@custom/dashboard` that depends on multiple resources:
|
||||
|
||||
```json title="dashboard.json" showLineNumbers
|
||||
{
|
||||
"name": "dashboard",
|
||||
"registryDependencies": [
|
||||
"@shadcn/card", // 1. Resolved first
|
||||
"@vendor/chart", // 2. Resolved second
|
||||
"@custom/card" // 3. Resolved last (overrides @shadcn/card)
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Resolution order:
|
||||
|
||||
1. `@shadcn/card` - installs to `components/ui/card.tsx`
|
||||
2. `@vendor/chart` - installs to `components/ui/chart.tsx`
|
||||
3. `@custom/card` - overwrites `components/ui/card.tsx` (if same target)
|
||||
|
||||
### Key Resolution Features
|
||||
|
||||
1. **Source Tracking**: Each resource knows which registry it came from, avoiding naming conflicts
|
||||
2. **Circular Dependency Prevention**: Automatically detects and prevents circular dependencies
|
||||
3. **Smart Installation Order**: Dependencies are installed first, then the resources that use them
|
||||
|
||||
We intentionally designed the namespace system to be decentralized. There is no central registrar for namespaces. You are free to create and use any namespace you want.
|
||||
|
||||
This decentralized approach gives you complete flexibility to organize your resources however makes sense for your organization.
|
||||
|
||||
You can create multiple registries for different purposes:
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"registries": {
|
||||
"@acme-ui": "https://registry.acme.com/ui/{name}.json",
|
||||
"@acme-docs": "https://registry.acme.com/docs/{name}.json",
|
||||
"@acme-ai": "https://registry.acme.com/ai/{name}.json",
|
||||
"@acme-themes": "https://registry.acme.com/themes/{name}.json",
|
||||
"@acme-internal": {
|
||||
"url": "https://internal.acme.com/registry/{name}.json",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${INTERNAL_TOKEN}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This allows you to:
|
||||
|
||||
- **Organize by type**: Separate UI components, documentation, AI resources, etc.
|
||||
- **Organize by team**: Different teams can maintain their own registries
|
||||
- **Organize by visibility**: Public vs. private resources
|
||||
- **Organize by version**: Stable vs. experimental registries
|
||||
- **No naming conflicts**: Since there's no central authority, you don't need to worry about namespace collisions
|
||||
|
||||
### Examples of Multi-Registry Setups
|
||||
|
||||
#### By Resource Type
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"@components": "https://cdn.company.com/components/{name}.json",
|
||||
"@hooks": "https://cdn.company.com/hooks/{name}.json",
|
||||
"@utils": "https://cdn.company.com/utils/{name}.json",
|
||||
"@prompts": "https://cdn.company.com/ai-prompts/{name}.json"
|
||||
}
|
||||
```
|
||||
|
||||
#### By Team or Department
|
||||
|
||||
```json
|
||||
{
|
||||
"@design": "https://design.company.com/registry/{name}.json",
|
||||
"@engineering": "https://eng.company.com/registry/{name}.json",
|
||||
"@marketing": "https://marketing.company.com/registry/{name}.json"
|
||||
}
|
||||
```
|
||||
|
||||
#### By Stability
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"@stable": "https://registry.company.com/stable/{name}.json",
|
||||
"@latest": "https://registry.company.com/beta/{name}.json",
|
||||
"@experimental": "https://registry.company.com/experimental/{name}.json"
|
||||
}
|
||||
```
|
||||
|
||||
## Built-in Registries
|
||||
|
||||
The `@shadcn` namespace is built-in and always available:
|
||||
|
||||
```bash
|
||||
npx shadcn@beta add @shadcn/button
|
||||
```
|
||||
|
||||
This is equivalent to installing from the default shadcn/ui registry.
|
||||
|
||||
## Versioning
|
||||
|
||||
You can implement versioning for your registry resources using query parameters. This allows users to pin specific versions or use different release channels.
|
||||
|
||||
### Basic Version Parameter
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"@versioned": {
|
||||
"url": "https://registry.example.com/{name}",
|
||||
"params": {
|
||||
"version": "v2"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This resolves `@versioned/button` to: `https://registry.example.com/button?version=v2`
|
||||
|
||||
### Dynamic Version Selection
|
||||
|
||||
Use environment variables to control versions across your project:
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"@stable": {
|
||||
"url": "https://registry.company.com/{name}",
|
||||
"params": {
|
||||
"version": "${REGISTRY_VERSION}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This allows you to:
|
||||
|
||||
- Set `REGISTRY_VERSION=v1.2.3` in production
|
||||
- Override per environment (dev, staging, prod)
|
||||
|
||||
### Semantic Versioning
|
||||
|
||||
Implement semantic versioning with range support:
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"@npm-style": {
|
||||
"url": "https://registry.example.com/{name}",
|
||||
"params": {
|
||||
"semver": "^2.0.0",
|
||||
"prerelease": "${ALLOW_PRERELEASE}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Version Resolution Best Practices
|
||||
|
||||
1. **Use environment variables** for version control across environments
|
||||
2. **Provide sensible defaults** using the `${VAR:-default}` syntax
|
||||
3. **Document version schemes** clearly for registry users
|
||||
4. **Support version pinning** for reproducible builds
|
||||
5. **Implement version discovery** endpoints (e.g., `/versions/{name}`)
|
||||
6. **Cache versioned resources** appropriately with proper cache headers
|
||||
|
||||
## CLI Commands
|
||||
|
||||
The shadcn CLI provides several commands for working with namespaced registries:
|
||||
|
||||
### Adding Resources
|
||||
|
||||
Install resources from any configured registry:
|
||||
|
||||
```bash
|
||||
# Install from a specific registry
|
||||
npx shadcn@beta add @v0/dashboard
|
||||
|
||||
# Install multiple resources
|
||||
npx shadcn@beta add @acme/button @lib/utils @ai/prompt
|
||||
|
||||
# Install from URL directly
|
||||
npx shadcn@beta add https://registry.example.com/button.json
|
||||
|
||||
# Install from local file
|
||||
npx shadcn@beta add ./local-registry/button.json
|
||||
```
|
||||
|
||||
### Viewing Resources
|
||||
|
||||
Inspect registry items before installation:
|
||||
|
||||
```bash
|
||||
# View a resource from a registry
|
||||
npx shadcn@beta view @acme/button
|
||||
|
||||
# View multiple resources
|
||||
npx shadcn@beta view @v0/dashboard @shadcn/card
|
||||
|
||||
# View from URL
|
||||
npx shadcn@beta view https://registry.example.com/button.json
|
||||
```
|
||||
|
||||
The `view` command displays:
|
||||
|
||||
- Resource metadata (name, type, description)
|
||||
- Dependencies and registry dependencies
|
||||
- File contents that will be installed
|
||||
- CSS variables and Tailwind configuration
|
||||
- Required environment variables
|
||||
|
||||
### Searching Registries
|
||||
|
||||
Search for available resources in registries:
|
||||
|
||||
```bash
|
||||
# Search a specific registry
|
||||
npx shadcn@beta search @v0
|
||||
|
||||
# Search with query
|
||||
npx shadcn@beta search @acme --query "auth"
|
||||
|
||||
# Search multiple registries
|
||||
npx shadcn@beta search @v0 @acme @lib
|
||||
|
||||
# Limit results
|
||||
npx shadcn@beta search @v0 --limit 10 --offset 20
|
||||
|
||||
# List all items (alias for search)
|
||||
npx shadcn@beta list @acme
|
||||
```
|
||||
|
||||
Search results include:
|
||||
|
||||
- Resource name and type
|
||||
- Description
|
||||
- Registry source
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Registry Not Configured
|
||||
|
||||
If you reference a registry that isn't configured:
|
||||
|
||||
```bash
|
||||
npx shadcn@beta add @non-existent/component
|
||||
```
|
||||
|
||||
Error:
|
||||
|
||||
```txt
|
||||
Unknown registry "@non-existent". Make sure it is defined in components.json as follows:
|
||||
{
|
||||
"registries": {
|
||||
"@non-existent": "[URL_TO_REGISTRY]"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Missing Environment Variables
|
||||
|
||||
If required environment variables are not set:
|
||||
|
||||
```txt
|
||||
Registry "@private" requires the following environment variables:
|
||||
|
||||
• REGISTRY_TOKEN
|
||||
|
||||
Set the required environment variables to your .env or .env.local file.
|
||||
```
|
||||
|
||||
### Resource Not Found
|
||||
|
||||
404 Not Found:
|
||||
|
||||
```txt
|
||||
The item at https://registry.company.com/button.json was not found. It may not exist at the registry.
|
||||
```
|
||||
|
||||
This usually means:
|
||||
|
||||
- The resource name is misspelled
|
||||
- The resource doesn't exist in the registry
|
||||
- The registry URL pattern is incorrect
|
||||
|
||||
### Authentication Failures
|
||||
|
||||
401 Unauthorized:
|
||||
|
||||
```txt
|
||||
You are not authorized to access the item at https://api.company.com/button.json
|
||||
Check your authentication credentials and environment variables.
|
||||
```
|
||||
|
||||
403 Forbidden:
|
||||
|
||||
```txt
|
||||
Access forbidden for https://api.company.com/button.json
|
||||
Verify your API key has the necessary permissions.
|
||||
```
|
||||
|
||||
## Creating Your Own Registry
|
||||
|
||||
To make your registry compatible with the namespace system, you can serve any type of resource - components, libraries, utilities, AI prompts, themes, configurations, or any other shareable code/content:
|
||||
|
||||
1. **Implement the registry item schema**: Your registry must return JSON that conforms to the [registry item schema](/docs/registry/registry-item-json).
|
||||
|
||||
2. **Support the URL pattern**: Include `{name}` in your URL template where the resource name will be inserted.
|
||||
|
||||
3. **Define resource types**: Use appropriate `type` fields to identify your resources (e.g., `registry:ui`, `registry:lib`, `registry:ai`, `registry:theme`, etc.).
|
||||
|
||||
4. **Handle authentication** (if needed): Accept authentication via headers or query parameters.
|
||||
|
||||
5. **Document your namespace**: Provide clear instructions for users to configure your registry:
|
||||
|
||||
```json title="components.json" showLineNumbers
|
||||
{
|
||||
"registries": {
|
||||
"@your-registry": "https://your-domain.com/r/{name}.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Parser Pattern
|
||||
|
||||
The namespace parser uses the following regex pattern:
|
||||
|
||||
```regex title="namespace-parser.js"
|
||||
/^(@[a-zA-Z0-9](?:[a-zA-Z0-9-_]*[a-zA-Z0-9])?)\/(.+)$/
|
||||
```
|
||||
|
||||
This ensures valid namespace formatting and proper component name extraction.
|
||||
|
||||
### Resolution Process
|
||||
|
||||
1. **Parse**: Extract namespace and component name from `@namespace/component`
|
||||
2. **Lookup**: Find registry configuration for `@namespace`
|
||||
3. **Build URL**: Replace placeholders with actual values
|
||||
4. **Set Headers**: Apply authentication headers if configured
|
||||
5. **Fetch**: Retrieve component from the resolved URL
|
||||
6. **Validate**: Ensure response matches registry item schema
|
||||
7. **Resolve Dependencies**: Recursively fetch any registry dependencies
|
||||
|
||||
### Cross-Registry Dependencies
|
||||
|
||||
When a component has dependencies from different registries, the resolver:
|
||||
|
||||
1. Maintains separate authentication contexts for each registry
|
||||
2. Resolves each dependency from its respective source
|
||||
3. Deduplicates files based on target paths
|
||||
4. Merges configurations (tailwind, cssVars, etc.) from all sources
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use environment variables** for sensitive data like API keys and tokens
|
||||
2. **Namespace your registry** with a unique, descriptive name
|
||||
3. **Document authentication requirements** clearly for users
|
||||
4. **Implement proper error responses** with helpful messages
|
||||
5. **Cache registry responses** when possible to improve performance
|
||||
6. **Support style variants** if your components have multiple themes
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Resources not found
|
||||
|
||||
- Verify the registry URL is correct and accessible
|
||||
- Check that the `{name}` placeholder is included in the URL
|
||||
- Ensure the resource exists in the registry
|
||||
- Confirm the resource type matches what the registry provides
|
||||
|
||||
### Authentication issues
|
||||
|
||||
- Confirm environment variables are set correctly
|
||||
- Verify API keys/tokens are valid and not expired
|
||||
- Check that headers are being sent in the correct format
|
||||
|
||||
### Dependency conflicts
|
||||
|
||||
- Review resources with the same name from different registries
|
||||
- Use fully qualified names (`@namespace/resource`) to avoid ambiguity
|
||||
- Check for circular dependencies between registries
|
||||
- Ensure resource types are compatible when mixing registries
|
||||
@@ -8,8 +8,8 @@ If your registry is hosted and publicly accessible via a URL, you can open a reg
|
||||
eg. [https://v0.dev/chat/api/open?url=https://ui.shadcn.com/r/styles/new-york/login-01.json](https://v0.dev/chat/api/open?url=https://ui.shadcn.com/r/styles/new-york/login-01.json)
|
||||
|
||||
<Callout className="mt-6">
|
||||
**Note:** The `Open in v0` button does not support `cssVars` and `tailwind`
|
||||
properties.
|
||||
**Important:** `Open in v0` does not support `cssVars`, `css`, `envVars`,
|
||||
namespaced registries, or advanced authentication methods.
|
||||
</Callout>
|
||||
|
||||
## Button
|
||||
@@ -61,4 +61,46 @@ export function OpenInV0Button({ url }: { url: string }) {
|
||||
|
||||
## Authentication
|
||||
|
||||
See the [Adding Auth](/docs/registry/getting-started#adding-auth) section for more information on how to authenticate requests to your registry and Open in v0.
|
||||
Open in v0 only supports query parameter authentication. It does not support namespaced registries or advanced authentication methods like Bearer tokens or API keys in headers.
|
||||
|
||||
### Using Query Parameter Authentication
|
||||
|
||||
To add authentication to your registry for Open in v0, use a `token` query parameter:
|
||||
|
||||
```
|
||||
https://registry.company.com/r/hello-world.json?token=your_secure_token_here
|
||||
```
|
||||
|
||||
When implementing this on your registry server:
|
||||
|
||||
1. Check for the `token` query parameter
|
||||
2. Validate the token against your authentication system
|
||||
3. Return a `401 Unauthorized` response if the token is invalid or missing
|
||||
4. Both the shadcn CLI and Open in v0 will handle the 401 response and display an appropriate message to users
|
||||
|
||||
### Example Implementation
|
||||
|
||||
```typescript
|
||||
// Next.js API route example
|
||||
export async function GET(request: NextRequest) {
|
||||
const token = request.nextUrl.searchParams.get('token')
|
||||
|
||||
if (!isValidToken(token)) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "Unauthorized",
|
||||
message: "Invalid or missing token"
|
||||
},
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
// Return the registry item
|
||||
return NextResponse.json(registryItem)
|
||||
}
|
||||
```
|
||||
|
||||
<Callout className="mt-6">
|
||||
**Security Note:** Make sure to encrypt and expire tokens. Never expose
|
||||
production tokens in documentation or examples.
|
||||
</Callout>
|
||||
|
||||
@@ -12,6 +12,12 @@ The `registry-item.json` schema is used to define your custom registry items.
|
||||
"type": "registry:block",
|
||||
"title": "Hello World",
|
||||
"description": "A simple hello world component.",
|
||||
"registryDependencies": [
|
||||
"button",
|
||||
"@acme/input-form",
|
||||
"https://example.com/r/foo"
|
||||
],
|
||||
"dependencies": ["is-even@3.0.0", "motion"],
|
||||
"files": [
|
||||
{
|
||||
"path": "registry/new-york/hello-world/hello-world.tsx",
|
||||
@@ -107,6 +113,7 @@ The following types are supported:
|
||||
| `registry:file` | Use for miscellaneous files. |
|
||||
| `registry:style` | Use for registry styles. eg. `new-york` |
|
||||
| `registry:theme` | Use for themes. |
|
||||
| `registry:item` | Use for universal registry items. |
|
||||
|
||||
### author
|
||||
|
||||
@@ -139,17 +146,17 @@ Use `@version` to specify the version of your registry item.
|
||||
|
||||
### registryDependencies
|
||||
|
||||
Used for registry dependencies. Can be names or URLs. Use the name of the item to reference shadcn/ui components and urls to reference other registries.
|
||||
Used for registry dependencies. Can be names, namespaced or URLs.
|
||||
|
||||
- For `shadcn/ui` registry items such as `button`, `input`, `select`, etc use the name eg. `['button', 'input', 'select']`.
|
||||
- For namespaced registry items such as `@acme` use the name eg. `['@acme/input-form']`.
|
||||
- For custom registry items use the URL of the registry item eg. `['https://example.com/r/hello-world.json']`.
|
||||
|
||||
```json title="registry-item.json" showLineNumbers
|
||||
{
|
||||
"registryDependencies": [
|
||||
"button",
|
||||
"input",
|
||||
"select",
|
||||
"@acme/input-form",
|
||||
"https://example.com/r/editor.json"
|
||||
]
|
||||
}
|
||||
@@ -260,11 +267,13 @@ Use to define CSS variables for your registry item.
|
||||
|
||||
### css
|
||||
|
||||
Use `css` to add new rules to the project's CSS file eg. `@layer base`, `@layer components`, `@utility`, `@keyframes`, etc.
|
||||
Use `css` to add new rules to the project's CSS file eg. `@layer base`, `@layer components`, `@utility`, `@keyframes`, `@plugin`, etc.
|
||||
|
||||
```json title="registry-item.json" showLineNumbers
|
||||
{
|
||||
"css": {
|
||||
"@plugin @tailwindcss/typography": {},
|
||||
"@plugin foo": {},
|
||||
"@layer base": {
|
||||
"body": {
|
||||
"font-size": "var(--text-base)",
|
||||
@@ -293,13 +302,35 @@ Use `css` to add new rules to the project's CSS file eg. `@layer base`, `@layer
|
||||
}
|
||||
```
|
||||
|
||||
### envVars
|
||||
|
||||
Use `envVars` to add environment variables to your registry item.
|
||||
|
||||
```json title="registry-item.json" showLineNumbers
|
||||
{
|
||||
"envVars": {
|
||||
"NEXT_PUBLIC_APP_URL": "http://localhost:4000",
|
||||
"DATABASE_URL": "postgresql://postgres:postgres@localhost:5432/postgres",
|
||||
"OPENAI_API_KEY": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Environment variables are added to the `.env.local` or `.env` file. Existing variables are not overwritten.
|
||||
|
||||
<Callout>
|
||||
|
||||
**IMPORTANT:** Use `envVars` to add development or example variables. Do NOT use it to add production variables.
|
||||
|
||||
</Callout>
|
||||
|
||||
### docs
|
||||
|
||||
Use `docs` to show custom documentation or message when installing your registry item via the CLI.
|
||||
|
||||
```json title="registry-item.json" showLineNumbers
|
||||
{
|
||||
"docs": "Remember to add the FOO_BAR environment variable to your .env file."
|
||||
"docs": "To get an OPENAI_API_KEY, sign up for an account at https://platform.openai.com."
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -16,6 +16,12 @@ The `registry.json` schema is used to define your custom component registry.
|
||||
"type": "registry:block",
|
||||
"title": "Hello World",
|
||||
"description": "A simple hello world component.",
|
||||
"registryDependencies": [
|
||||
"button",
|
||||
"@acme/input-form",
|
||||
"https://example.com/r/foo"
|
||||
],
|
||||
"dependencies": ["is-even@3.0.0", "motion"],
|
||||
"files": [
|
||||
{
|
||||
"path": "registry/new-york/hello-world/hello-world.tsx",
|
||||
@@ -73,6 +79,12 @@ The `items` in your registry. Each item must implement the [registry-item schema
|
||||
"type": "registry:block",
|
||||
"title": "Hello World",
|
||||
"description": "A simple hello world component.",
|
||||
"registryDependencies": [
|
||||
"button",
|
||||
"@acme/input-form",
|
||||
"https://example.com/r/foo"
|
||||
],
|
||||
"dependencies": ["is-even@3.0.0", "motion"],
|
||||
"files": [
|
||||
{
|
||||
"path": "registry/new-york/hello-world/hello-world.tsx",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use server"
|
||||
|
||||
import { registryItemSchema } from "shadcn/registry"
|
||||
import { registryItemSchema } from "shadcn/schema"
|
||||
import { z } from "zod"
|
||||
|
||||
export async function getAllBlockIds(
|
||||
|
||||
@@ -3,7 +3,7 @@ export const siteConfig = {
|
||||
url: "https://ui.shadcn.com",
|
||||
ogImage: "https://ui.shadcn.com/og.jpg",
|
||||
description:
|
||||
"A set of beautifully-designed, accessible components and a code distribution platform. Works with your favorite frameworks. Open Source. Open Code.",
|
||||
"A set of beautifully designed components that you can customize, extend, and build on. Start here then make it your own. Open Source. Open Code.",
|
||||
links: {
|
||||
twitter: "https://twitter.com/shadcn",
|
||||
github: "https://github.com/shadcn-ui/ui",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { promises as fs } from "fs"
|
||||
import { tmpdir } from "os"
|
||||
import path from "path"
|
||||
import { registryItemFileSchema, registryItemSchema } from "shadcn/registry"
|
||||
import { registryItemFileSchema, registryItemSchema } from "shadcn/schema"
|
||||
import { Project, ScriptKind } from "ts-morph"
|
||||
import { z } from "zod"
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ export const mdxComponents = {
|
||||
.replace(/\?/g, "")
|
||||
.toLowerCase()}
|
||||
className={cn(
|
||||
"font-heading mt-12 scroll-m-28 text-2xl font-medium tracking-tight first:mt-0 lg:mt-20 [&+p]:!mt-4",
|
||||
"font-heading mt-8 scroll-m-28 text-2xl font-medium tracking-tight first:mt-0 lg:mt-8 [&+p]:!mt-4 *:[code]:text-2xl",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -62,7 +62,7 @@ export const mdxComponents = {
|
||||
h3: ({ className, ...props }: React.ComponentProps<"h3">) => (
|
||||
<h3
|
||||
className={cn(
|
||||
"font-heading mt-8 scroll-m-28 text-xl font-semibold tracking-tight",
|
||||
"font-heading mt-8 scroll-m-28 text-xl font-medium tracking-tight *:[code]:text-xl",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -228,7 +228,7 @@ export const mdxComponents = {
|
||||
return (
|
||||
<code
|
||||
className={cn(
|
||||
"bg-muted relative rounded-md px-[0.3rem] py-[0.2rem] font-mono text-[0.8rem] outline-none",
|
||||
"bg-muted relative rounded-md px-[0.3rem] py-[0.2rem] font-mono text-[0.8rem] break-words outline-none",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -310,7 +310,7 @@ export const mdxComponents = {
|
||||
}: React.ComponentProps<typeof TabsTrigger>) => (
|
||||
<TabsTrigger
|
||||
className={cn(
|
||||
"text-muted-foreground data-[state=active]:text-foreground px-0 text-base data-[state=active]:shadow-none dark:data-[state=active]:border-transparent dark:data-[state=active]:bg-transparent",
|
||||
"text-muted-foreground data-[state=active]:text-foreground data-[state=active]:border-primary dark:data-[state=active]:border-primary rounded-none border-0 border-b-2 border-transparent bg-transparent px-0 pb-3 text-base data-[state=active]:bg-transparent data-[state=active]:shadow-none dark:data-[state=active]:bg-transparent",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -68,6 +68,24 @@ const nextConfig = {
|
||||
destination: "/view/:name",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/docs/:path*.mdx",
|
||||
destination: "/docs/:path*.md",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/mcp",
|
||||
destination: "/docs/mcp",
|
||||
permanent: false,
|
||||
},
|
||||
]
|
||||
},
|
||||
rewrites() {
|
||||
return [
|
||||
{
|
||||
source: "/docs/:path*.md",
|
||||
destination: "/llm/:path*",
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"@radix-ui/react-toggle-group": "^1.1.1",
|
||||
"@radix-ui/react-tooltip": "^1.1.7",
|
||||
"@tabler/icons-react": "^3.31.0",
|
||||
"@tailwindcss/postcss": "^4.0.1",
|
||||
"@tailwindcss/postcss": "^4.1.11",
|
||||
"@tanstack/react-table": "^8.9.1",
|
||||
"@vercel/analytics": "^1.4.1",
|
||||
"change-case": "^5.4.4",
|
||||
@@ -65,10 +65,10 @@
|
||||
"date-fns": "^4.1.0",
|
||||
"embla-carousel-autoplay": "8.5.2",
|
||||
"embla-carousel-react": "8.5.2",
|
||||
"fumadocs-core": "^15.3.1",
|
||||
"fumadocs-docgen": "^2.0.0",
|
||||
"fumadocs-mdx": "^11.6.3",
|
||||
"fumadocs-ui": "^15.3.1",
|
||||
"fumadocs-core": "15.3.1",
|
||||
"fumadocs-docgen": "2.0.0",
|
||||
"fumadocs-mdx": "11.6.3",
|
||||
"fumadocs-ui": "15.3.1",
|
||||
"input-otp": "^1.4.2",
|
||||
"jotai": "^2.1.0",
|
||||
"little-date": "^1.0.0",
|
||||
@@ -86,7 +86,7 @@
|
||||
"recharts": "2.15.1",
|
||||
"rehype-pretty-code": "^0.14.1",
|
||||
"rimraf": "^6.0.1",
|
||||
"shadcn": "2.6.1",
|
||||
"shadcn": "2.10.0",
|
||||
"shiki": "^1.10.1",
|
||||
"sonner": "^2.0.0",
|
||||
"tailwind-merge": "^3.0.1",
|
||||
@@ -108,7 +108,7 @@
|
||||
"eslint-config-next": "15.3.1",
|
||||
"prettier": "^3.4.2",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"tailwindcss": "^4.1.7",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"tw-animate-css": "^1.2.4",
|
||||
"typescript": "^5",
|
||||
"unist-builder": "3.0.0",
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
"name": "chart",
|
||||
"type": "registry:ui",
|
||||
"dependencies": [
|
||||
"recharts",
|
||||
"recharts@2.15.4",
|
||||
"lucide-react"
|
||||
],
|
||||
"registryDependencies": [
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
|
||||
"type": "registry:ui",
|
||||
"author": "shadcn (https://ui.shadcn.com)",
|
||||
"dependencies": [
|
||||
"recharts",
|
||||
"recharts@2.15.4",
|
||||
"lucide-react"
|
||||
],
|
||||
"registryDependencies": [
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"name": "default",
|
||||
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
||||
"name": "index",
|
||||
"type": "registry:style",
|
||||
"author": "shadcn (https://ui.shadcn.com)",
|
||||
"dependencies": [
|
||||
"tailwindcss-animate",
|
||||
"class-variance-authority",
|
||||
@@ -9,6 +11,7 @@
|
||||
"registryDependencies": [
|
||||
"utils"
|
||||
],
|
||||
"files": [],
|
||||
"tailwind": {
|
||||
"config": {
|
||||
"plugins": [
|
||||
@@ -16,6 +19,5 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"cssVars": {},
|
||||
"files": []
|
||||
"cssVars": {}
|
||||
}
|
||||
5793
apps/v4/public/r/styles/default/registry.json
Normal file
5793
apps/v4/public/r/styles/default/registry.json
Normal file
File diff suppressed because it is too large
Load Diff
23
apps/v4/public/r/styles/default/style.json
Normal file
23
apps/v4/public/r/styles/default/style.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
||||
"name": "style",
|
||||
"type": "registry:style",
|
||||
"author": "shadcn (https://ui.shadcn.com)",
|
||||
"dependencies": [
|
||||
"tailwindcss-animate",
|
||||
"class-variance-authority",
|
||||
"lucide-react"
|
||||
],
|
||||
"registryDependencies": [
|
||||
"utils"
|
||||
],
|
||||
"files": [],
|
||||
"tailwind": {
|
||||
"config": {
|
||||
"plugins": [
|
||||
"require(\"tailwindcss-animate\")"
|
||||
]
|
||||
}
|
||||
},
|
||||
"cssVars": {}
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
"files": [
|
||||
{
|
||||
"path": "registry/new-york-v4/blocks/calendar-24.tsx",
|
||||
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronDownIcon } from \"lucide-react\"\n\nimport { Button } from \"@/registry/new-york-v4/ui/button\"\nimport { Calendar } from \"@/registry/new-york-v4/ui/calendar\"\nimport { Input } from \"@/registry/new-york-v4/ui/input\"\nimport { Label } from \"@/registry/new-york-v4/ui/label\"\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/registry/new-york-v4/ui/popover\"\n\nexport default function Calendar24() {\n const [open, setOpen] = React.useState(false)\n const [date, setDate] = React.useState<Date | undefined>(undefined)\n\n return (\n <div className=\"flex gap-4\">\n <div className=\"flex flex-col gap-3\">\n <Label htmlFor=\"date\" className=\"px-1\">\n Date\n </Label>\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n id=\"date\"\n className=\"w-32 justify-between font-normal\"\n >\n {date ? date.toLocaleDateString() : \"Select date\"}\n <ChevronDownIcon />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto overflow-hidden p-0\" align=\"start\">\n <Calendar\n mode=\"single\"\n selected={date}\n captionLayout=\"dropdown\"\n onSelect={(date) => {\n setDate(date)\n setOpen(false)\n }}\n />\n </PopoverContent>\n </Popover>\n </div>\n <div className=\"flex flex-col gap-3\">\n <Label htmlFor=\"time\" className=\"px-1\">\n Time\n </Label>\n <Input\n type=\"time\"\n id=\"time\"\n step=\"1\"\n defaultValue=\"10:30:00\"\n className=\"bg-background appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none\"\n />\n </div>\n </div>\n )\n}\n",
|
||||
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronDownIcon } from \"lucide-react\"\n\nimport { Button } from \"@/registry/new-york-v4/ui/button\"\nimport { Calendar } from \"@/registry/new-york-v4/ui/calendar\"\nimport { Input } from \"@/registry/new-york-v4/ui/input\"\nimport { Label } from \"@/registry/new-york-v4/ui/label\"\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/registry/new-york-v4/ui/popover\"\n\nexport default function Calendar24() {\n const [open, setOpen] = React.useState(false)\n const [date, setDate] = React.useState<Date | undefined>(undefined)\n\n return (\n <div className=\"flex gap-4\">\n <div className=\"flex flex-col gap-3\">\n <Label htmlFor=\"date-picker\" className=\"px-1\">\n Date\n </Label>\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n id=\"date-picker\"\n className=\"w-32 justify-between font-normal\"\n >\n {date ? date.toLocaleDateString() : \"Select date\"}\n <ChevronDownIcon />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto overflow-hidden p-0\" align=\"start\">\n <Calendar\n mode=\"single\"\n selected={date}\n captionLayout=\"dropdown\"\n onSelect={(date) => {\n setDate(date)\n setOpen(false)\n }}\n />\n </PopoverContent>\n </Popover>\n </div>\n <div className=\"flex flex-col gap-3\">\n <Label htmlFor=\"time-picker\" className=\"px-1\">\n Time\n </Label>\n <Input\n type=\"time\"\n id=\"time-picker\"\n step=\"1\"\n defaultValue=\"10:30:00\"\n className=\"bg-background appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none\"\n />\n </div>\n </div>\n )\n}\n",
|
||||
"type": "registry:component"
|
||||
}
|
||||
],
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -3,7 +3,7 @@
|
||||
"name": "chart",
|
||||
"type": "registry:ui",
|
||||
"dependencies": [
|
||||
"recharts",
|
||||
"recharts@2.15.4",
|
||||
"lucide-react"
|
||||
],
|
||||
"registryDependencies": [
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
5777
apps/v4/public/r/styles/new-york-v4/registry.json
Normal file
5777
apps/v4/public/r/styles/new-york-v4/registry.json
Normal file
File diff suppressed because it is too large
Load Diff
17
apps/v4/public/r/styles/new-york-v4/style.json
Normal file
17
apps/v4/public/r/styles/new-york-v4/style.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
||||
"name": "style",
|
||||
"type": "registry:style",
|
||||
"dependencies": [
|
||||
"class-variance-authority",
|
||||
"lucide-react"
|
||||
],
|
||||
"devDependencies": [
|
||||
"tw-animate-css"
|
||||
],
|
||||
"registryDependencies": [
|
||||
"utils"
|
||||
],
|
||||
"files": [],
|
||||
"cssVars": {}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
|
||||
"type": "registry:ui",
|
||||
"author": "shadcn (https://ui.shadcn.com)",
|
||||
"dependencies": [
|
||||
"recharts",
|
||||
"recharts@2.15.4",
|
||||
"lucide-react"
|
||||
],
|
||||
"registryDependencies": [
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"name": "new-york",
|
||||
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
||||
"name": "index",
|
||||
"type": "registry:style",
|
||||
"author": "shadcn (https://ui.shadcn.com)",
|
||||
"dependencies": [
|
||||
"tailwindcss-animate",
|
||||
"class-variance-authority",
|
||||
@@ -9,6 +11,7 @@
|
||||
"registryDependencies": [
|
||||
"utils"
|
||||
],
|
||||
"files": [],
|
||||
"tailwind": {
|
||||
"config": {
|
||||
"plugins": [
|
||||
@@ -16,6 +19,5 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"cssVars": {},
|
||||
"files": []
|
||||
"cssVars": {}
|
||||
}
|
||||
5793
apps/v4/public/r/styles/new-york/registry.json
Normal file
5793
apps/v4/public/r/styles/new-york/registry.json
Normal file
File diff suppressed because it is too large
Load Diff
23
apps/v4/public/r/styles/new-york/style.json
Normal file
23
apps/v4/public/r/styles/new-york/style.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
||||
"name": "style",
|
||||
"type": "registry:style",
|
||||
"author": "shadcn (https://ui.shadcn.com)",
|
||||
"dependencies": [
|
||||
"tailwindcss-animate",
|
||||
"class-variance-authority",
|
||||
"lucide-react"
|
||||
],
|
||||
"registryDependencies": [
|
||||
"utils"
|
||||
],
|
||||
"files": [],
|
||||
"tailwind": {
|
||||
"config": {
|
||||
"plugins": [
|
||||
"require(\"tailwindcss-animate\")"
|
||||
]
|
||||
}
|
||||
},
|
||||
"cssVars": {}
|
||||
}
|
||||
@@ -56,6 +56,42 @@
|
||||
}
|
||||
},
|
||||
"required": ["utils", "components"]
|
||||
},
|
||||
"registries": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^@": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "\\{name\\}"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"pattern": "\\{name\\}"
|
||||
},
|
||||
"params": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["url"]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["style", "tailwind", "rsc", "aliases"]
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
"registry:theme",
|
||||
"registry:page",
|
||||
"registry:file",
|
||||
"registry:style"
|
||||
"registry:style",
|
||||
"registry:item"
|
||||
],
|
||||
"description": "The type of the item. This is used to determine the type and target path of the item when resolved for a project."
|
||||
},
|
||||
@@ -79,7 +80,8 @@
|
||||
"registry:theme",
|
||||
"registry:page",
|
||||
"registry:file",
|
||||
"registry:style"
|
||||
"registry:style",
|
||||
"registry:item"
|
||||
],
|
||||
"description": "The type of the file. This is used to determine the type of the file when resolved for a project."
|
||||
},
|
||||
@@ -189,6 +191,13 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"envVars": {
|
||||
"type": "object",
|
||||
"description": "Environment variables required by the registry item. Key-value pairs that will be added to the project's .env file.",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"type": "object",
|
||||
"description": "Additional metadata for the registry item. This is an object with any key value pairs.",
|
||||
|
||||
@@ -18,6 +18,22 @@
|
||||
"files": [],
|
||||
"cssVars": {}
|
||||
},
|
||||
{
|
||||
"name": "style",
|
||||
"type": "registry:style",
|
||||
"dependencies": [
|
||||
"class-variance-authority",
|
||||
"lucide-react"
|
||||
],
|
||||
"devDependencies": [
|
||||
"tw-animate-css"
|
||||
],
|
||||
"registryDependencies": [
|
||||
"utils"
|
||||
],
|
||||
"files": [],
|
||||
"cssVars": {}
|
||||
},
|
||||
{
|
||||
"name": "accordion",
|
||||
"type": "registry:ui",
|
||||
@@ -169,7 +185,7 @@
|
||||
"name": "chart",
|
||||
"type": "registry:ui",
|
||||
"dependencies": [
|
||||
"recharts",
|
||||
"recharts@2.15.4",
|
||||
"lucide-react"
|
||||
],
|
||||
"registryDependencies": [
|
||||
|
||||
@@ -16,6 +16,16 @@ export const Index: Record<string, any> = {
|
||||
categories: undefined,
|
||||
meta: undefined,
|
||||
},
|
||||
"style": {
|
||||
name: "style",
|
||||
description: "",
|
||||
type: "registry:style",
|
||||
registryDependencies: ["utils"],
|
||||
files: [],
|
||||
component: null,
|
||||
categories: undefined,
|
||||
meta: undefined,
|
||||
},
|
||||
"accordion": {
|
||||
name: "accordion",
|
||||
description: "",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { registryItemSchema, type Registry } from "shadcn/registry"
|
||||
import { registryItemSchema, type Registry } from "shadcn/schema"
|
||||
import { z } from "zod"
|
||||
|
||||
import { blocks } from "@/registry/registry-blocks"
|
||||
@@ -19,6 +19,16 @@ const DEPRECATED_ITEMS = [
|
||||
"toast-with-title",
|
||||
]
|
||||
|
||||
// Shared between index and style for backward compatibility.
|
||||
const NEW_YORK_V4_STYLE = {
|
||||
type: "registry:style",
|
||||
dependencies: ["class-variance-authority", "lucide-react"],
|
||||
devDependencies: ["tw-animate-css"],
|
||||
registryDependencies: ["utils"],
|
||||
cssVars: {},
|
||||
files: [],
|
||||
}
|
||||
|
||||
export const registry = {
|
||||
name: "shadcn/ui",
|
||||
homepage: "https://ui.shadcn.com",
|
||||
@@ -26,12 +36,11 @@ export const registry = {
|
||||
[
|
||||
{
|
||||
name: "index",
|
||||
type: "registry:style",
|
||||
dependencies: ["class-variance-authority", "lucide-react"],
|
||||
devDependencies: ["tw-animate-css"],
|
||||
registryDependencies: ["utils"],
|
||||
cssVars: {},
|
||||
files: [],
|
||||
...NEW_YORK_V4_STYLE,
|
||||
},
|
||||
{
|
||||
name: "style",
|
||||
...NEW_YORK_V4_STYLE,
|
||||
},
|
||||
...ui,
|
||||
...blocks,
|
||||
|
||||
@@ -20,14 +20,14 @@ export default function Calendar24() {
|
||||
return (
|
||||
<div className="flex gap-4">
|
||||
<div className="flex flex-col gap-3">
|
||||
<Label htmlFor="date" className="px-1">
|
||||
<Label htmlFor="date-picker" className="px-1">
|
||||
Date
|
||||
</Label>
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
id="date"
|
||||
id="date-picker"
|
||||
className="w-32 justify-between font-normal"
|
||||
>
|
||||
{date ? date.toLocaleDateString() : "Select date"}
|
||||
@@ -48,12 +48,12 @@ export default function Calendar24() {
|
||||
</Popover>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<Label htmlFor="time" className="px-1">
|
||||
<Label htmlFor="time-picker" className="px-1">
|
||||
Time
|
||||
</Label>
|
||||
<Input
|
||||
type="time"
|
||||
id="time"
|
||||
id="time-picker"
|
||||
step="1"
|
||||
defaultValue="10:30:00"
|
||||
className="bg-background appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
|
||||
|
||||
@@ -257,7 +257,6 @@ export function ChartAreaInteractive() {
|
||||
/>
|
||||
<ChartTooltip
|
||||
cursor={false}
|
||||
defaultIndex={isMobile ? -1 : 10}
|
||||
content={
|
||||
<ChartTooltipContent
|
||||
labelFormatter={(value) => {
|
||||
|
||||
@@ -233,7 +233,7 @@ export default function DataTableDemo() {
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div className="rounded-md border">
|
||||
<div className="overflow-hidden rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
|
||||
@@ -73,7 +73,10 @@ function Calendar({
|
||||
"relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md",
|
||||
defaultClassNames.dropdown_root
|
||||
),
|
||||
dropdown: cn("absolute inset-0 opacity-0", defaultClassNames.dropdown),
|
||||
dropdown: cn(
|
||||
"absolute bg-popover inset-0 opacity-0",
|
||||
defaultClassNames.dropdown
|
||||
),
|
||||
caption_label: cn(
|
||||
"select-none font-medium",
|
||||
captionLayout === "label"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type Registry } from "shadcn/registry"
|
||||
import { type Registry } from "shadcn/schema"
|
||||
|
||||
export const blocks: Registry["items"] = [
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type Registry } from "shadcn/registry"
|
||||
import { type Registry } from "shadcn/schema"
|
||||
|
||||
export const charts: Registry["items"] = [
|
||||
// Area Charts
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type Registry } from "shadcn/registry"
|
||||
import { type Registry } from "shadcn/schema"
|
||||
|
||||
export const examples: Registry["items"] = [
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user