+
+
Use case
+
Example files
+
+ {[
+ ["Components", "components/date-picker.tsx", "components/data-table.tsx"],
+ [
+ "Helpers and utilities",
+ "lib/format-date.ts",
+ "lib/cn.ts",
+ "hooks/use-copy.ts",
+ ],
+ [
+ "Design system packages",
+ "tokens/colors.json",
+ "styles/theme.css",
+ "components/*",
+ ],
+ [
+ "Feature kits",
+ "app/(auth)/*",
+ "lib/auth.ts",
+ "components/login-form.tsx",
+ ],
+ ["Agent workflows", "AGENTS.md", ".cursor/rules/*", ".claude/commands/*"],
+ [
+ "Project conventions",
+ ".editorconfig",
+ "biome.json",
+ "docs/conventions.md",
+ ],
+ [
+ "Codemods and migration kits",
+ "codemods/*",
+ "scripts/migrate.ts",
+ "docs/migration.md",
+ ],
+ ["Testing setup", "vitest.config.ts", "test/setup.ts", "docs/testing.md"],
+ [
+ "CI and release workflows",
+ ".github/workflows/ci.yml",
+ ".github/workflows/release.yml",
+ ],
+ [
+ "Project automation",
+ "scripts/release.ts",
+ "scripts/checks.ts",
+ "docs/automation.md",
+ ],
+ [
+ "Issue and pull request templates",
+ ".github/ISSUE_TEMPLATE/*",
+ ".github/pull_request_template.md",
+ ],
+ ["MCP configuration", ".mcp.json", ".cursor/mcp.json"],
+ ].map(([label, ...files]) => (
+
+
{label}
+
+ {files.map((file) => (
+ {file}
+ ))}
+
+
+ ))}
+
+
+## When to use GitHub
+
+Use a GitHub registry when:
+
+- You already have reusable code in a public GitHub repository.
+- You want users to install directly from `owner/repo/item`.
+- You want to distribute config files, rules, docs, templates, utilities or
+ any other files from the same repository.
+- You do not need private repo access or custom request authentication.
+
+## Requirements
+
+A GitHub registry must:
+
+- Be a public `github.com` repository.
+- Have a `registry.json` file at the repository root.
+- Use valid `registry.json` and `registry-item.json` schemas.
+- Reference source files that exist in the repository.
+
+Private repositories and GitHub Enterprise hosts are not currently supported by
+GitHub addresses. For private or authenticated registries, use a
+[namespace](/docs/registry/namespace) with
+[authentication](/docs/registry/authentication).
+
+## Step 1: Add registry.json
+
+Given an existing public repository:
+
+```txt
+.
+├── ...
+├── .editorconfig
+├── AGENTS.md
+└── docs
+ └── conventions.md
+```
+
+Add `registry.json` at the root of the repository.
+
+```txt
+.
+├── ...
+├── registry.json
+├── .editorconfig
+├── AGENTS.md
+└── docs
+ └── conventions.md
+```
+
+Define the item you want to distribute.
+
+```json title="registry.json" showLineNumbers
+{
+ "$schema": "https://ui.shadcn.com/schema/registry.json",
+ "name": "acme-toolkit",
+ "homepage": "https://github.com/acme/toolkit",
+ "items": [
+ {
+ "name": "project-conventions",
+ "type": "registry:item",
+ "title": "Project Conventions",
+ "description": "Shared project conventions, editor settings and agent instructions.",
+ "files": [
+ {
+ "path": "AGENTS.md",
+ "type": "registry:file",
+ "target": "~/AGENTS.md"
+ },
+ {
+ "path": ".editorconfig",
+ "type": "registry:file",
+ "target": "~/.editorconfig"
+ },
+ {
+ "path": "docs/conventions.md",
+ "type": "registry:file",
+ "target": "~/docs/conventions.md"
+ }
+ ]
+ }
+ ]
+}
+```
+
+Commit and push the file.
+
+```bash
+git add registry.json
+```
+
+```bash
+git commit -m "add registry"
+```
+
+```bash
+git push
+```
+
+Users can now install the item from GitHub.
+
+```bash
+npx shadcn@latest add acme/toolkit/project-conventions
+```
+
+## Step 2: Distribute any file
+
+A registry item can install one file or many files. Use the `files` array to
+declare the files that belong together.
+
+For example, a testing setup can install a Vitest config, a setup file and a
+short team guide.
+
+```txt
+registry.json
+config
+└── vitest.config.ts
+docs
+└── testing.md
+test
+└── setup.ts
+```
+
+```json title="registry.json" showLineNumbers
+{
+ "$schema": "https://ui.shadcn.com/schema/registry.json",
+ "name": "acme-toolkit",
+ "homepage": "https://github.com/acme/toolkit",
+ "items": [
+ {
+ "name": "vitest-setup",
+ "type": "registry:item",
+ "title": "Vitest Setup",
+ "description": "A Vitest setup with project defaults and docs.",
+ "files": [
+ {
+ "path": "config/vitest.config.ts",
+ "type": "registry:file",
+ "target": "~/vitest.config.ts"
+ },
+ {
+ "path": "test/setup.ts",
+ "type": "registry:file",
+ "target": "~/test/setup.ts"
+ },
+ {
+ "path": "docs/testing.md",
+ "type": "registry:file",
+ "target": "~/docs/testing.md"
+ }
+ ]
+ }
+ ]
+}
+```
+
+Users install it the same way.
+
+```bash
+npx shadcn@latest add acme/toolkit/vitest-setup
+```
+
+Use `target` when a file should be written to a specific destination in the
+user's project.
+
+```json title="registry.json" showLineNumbers
+{
+ "$schema": "https://ui.shadcn.com/schema/registry.json",
+ "name": "acme-toolkit",
+ "homepage": "https://github.com/acme/toolkit",
+ "items": [
+ {
+ "name": "editorconfig",
+ "type": "registry:file",
+ "files": [
+ {
+ "path": "config/.editorconfig",
+ "type": "registry:file",
+ "target": "~/.editorconfig"
+ }
+ ]
+ }
+ ]
+}
+```
+
+```bash
+npx shadcn@latest add acme/toolkit/editorconfig
+```
+
+## Step 3: Validate the registry
+
+Before sharing the registry, validate it from the CLI.
+
+```bash
+npx shadcn@latest registry validate acme/toolkit
+```
+
+The command reads the root `registry.json`, resolves includes, validates the
+registry items, and checks that referenced files exist.
+
+You can also validate a branch, tag or commit SHA.
+
+```bash
+npx shadcn@latest registry validate acme/toolkit#v1.0.0
+```
+
+## Step 4: List and search items
+
+Use `list` to see every item in the repository registry.
+
+```bash
+npx shadcn@latest list acme/toolkit
+```
+
+Use `search` to filter the catalog.
+
+```bash
+npx shadcn@latest search acme/toolkit --query conventions
+```
+
+Use `view` to inspect one item payload.
+
+```bash
+npx shadcn@latest view acme/toolkit/project-conventions
+```
+
+## Organize with include
+
+For larger repositories, keep item definitions close to the source files they
+describe.
+
+```txt
+registry.json
+config
+├── prettier.config.mjs
+└── registry.json
+rules
+├── agent.md
+└── registry.json
+```
+
+The root `registry.json` can include the nested registry files.
+
+```json title="registry.json" showLineNumbers
+{
+ "$schema": "https://ui.shadcn.com/schema/registry.json",
+ "name": "acme-toolkit",
+ "homepage": "https://github.com/acme/toolkit",
+ "include": ["config/registry.json", "rules/registry.json"]
+}
+```
+
+The included registry file declares items for that directory.
+
+```json title="rules/registry.json" showLineNumbers
+{
+ "$schema": "https://ui.shadcn.com/schema/registry.json",
+ "items": [
+ {
+ "name": "agent-rules",
+ "type": "registry:file",
+ "files": [
+ {
+ "path": "agent.md",
+ "type": "registry:file",
+ "target": "~/AGENTS.md"
+ }
+ ]
+ }
+ ]
+}
+```
+
+When using `include`, file paths are relative to the `registry.json` file that
+declares the item.
+
+```bash
+npx shadcn@latest add acme/toolkit/project-conventions
+```
+
+## Registry dependencies
+
+Use `registryDependencies` when one registry item depends on another registry
+item.
+
+### Same repository dependencies
+
+For dependencies in the same GitHub repository, use the full GitHub item
+address.
+
+```json title="registry.json" showLineNumbers
+{
+ "$schema": "https://ui.shadcn.com/schema/registry.json",
+ "name": "acme-toolkit",
+ "homepage": "https://github.com/acme/toolkit",
+ "items": [
+ {
+ "name": "project-setup",
+ "type": "registry:item",
+ "registryDependencies": [
+ "acme/toolkit/agent-rules",
+ "acme/toolkit/prettier-config",
+ "acme/toolkit/tsconfig"
+ ],
+ "files": [
+ {
+ "path": "docs/project-setup.md",
+ "type": "registry:file",
+ "target": "~/docs/project-setup.md"
+ }
+ ]
+ }
+ ]
+}
+```
+
+A docs item can depend on a template item from the same repository.
+
+```json title="registry.json" showLineNumbers
+{
+ "$schema": "https://ui.shadcn.com/schema/registry.json",
+ "name": "acme-toolkit",
+ "homepage": "https://github.com/acme/toolkit",
+ "items": [
+ {
+ "name": "contributing-guide",
+ "type": "registry:item",
+ "registryDependencies": ["acme/toolkit/readme-template"],
+ "files": [
+ {
+ "path": "docs/contributing.md",
+ "type": "registry:file",
+ "target": "~/docs/contributing.md"
+ }
+ ]
+ }
+ ]
+}
+```
+
+A CI setup can depend on the same formatting and testing defaults that users can
+install separately.
+
+```json title="registry.json" showLineNumbers
+{
+ "$schema": "https://ui.shadcn.com/schema/registry.json",
+ "name": "acme-toolkit",
+ "homepage": "https://github.com/acme/toolkit",
+ "items": [
+ {
+ "name": "ci-setup",
+ "type": "registry:item",
+ "registryDependencies": [
+ "acme/toolkit/prettier-config",
+ "acme/toolkit/vitest-setup"
+ ],
+ "files": [
+ {
+ "path": ".github/workflows/ci.yml",
+ "type": "registry:file",
+ "target": "~/.github/workflows/ci.yml"
+ }
+ ]
+ }
+ ]
+}
+```
+
+### External registry dependencies
+
+Items can also depend on external registries. Use the full item address for the
+registry that owns the dependency.
+
+```json title="registry.json" showLineNumbers
+{
+ "$schema": "https://ui.shadcn.com/schema/registry.json",
+ "name": "acme-toolkit",
+ "homepage": "https://github.com/acme/toolkit",
+ "items": [
+ {
+ "name": "workspace-setup",
+ "type": "registry:item",
+ "registryDependencies": [
+ "@acme/tsconfig",
+ "contoso/devtools/prettier-config"
+ ],
+ "files": [
+ {
+ "path": "docs/workspace.md",
+ "type": "registry:file",
+ "target": "~/docs/workspace.md"
+ }
+ ]
+ }
+ ]
+}
+```
+
+### Dependency refs
+
+Refs are not inherited across dependencies. If a dependency should be pinned,
+include its own ref.
+
+```json title="registry.json" showLineNumbers
+{
+ "$schema": "https://ui.shadcn.com/schema/registry.json",
+ "name": "acme-toolkit",
+ "homepage": "https://github.com/acme/toolkit",
+ "items": [
+ {
+ "name": "project-setup",
+ "type": "registry:item",
+ "registryDependencies": [
+ "acme/toolkit/agent-rules#v1.0.0",
+ "acme/toolkit/tsconfig#c0ffee254729296a45d6691db565cf707a3fef5d"
+ ],
+ "files": [
+ {
+ "path": "docs/project-setup.md",
+ "type": "registry:file",
+ "target": "~/docs/project-setup.md"
+ }
+ ]
+ }
+ ]
+}
+```
+
+## Useful commands
+
+List every item in a GitHub registry.
+
+```bash
+npx shadcn@latest list acme/toolkit
+```
+
+Search a GitHub registry.
+
+```bash
+npx shadcn@latest search acme/toolkit -q conventions
+```
+
+Validate a GitHub registry.
+
+```bash
+npx shadcn@latest registry validate acme/toolkit
+```
+
+Install an item from a GitHub registry.
+
+```bash
+npx shadcn@latest add acme/toolkit/project-conventions
+```
+
+View an item from a GitHub registry.
+
+```bash
+npx shadcn@latest view acme/toolkit/project-conventions
+```
+
+Install an item whose registry item name contains `/`.
+
+```bash
+npx shadcn@latest add acme/toolkit/rules/agent
+```
+
+
-
- Getting Started
-
- Set up and build your own registry
-
-
+
+ Getting Started
+
+ Set up and build your own registry
+
+
+
+
+ GitHub
+
+ Turn a GitHub repository into a registry
+
+
+
+
+ Namespaces
+
+ Configure registries with namespaces
+
+
-
- Namespaces
-
- Configure registries with namespaces
-
-
+
Examples
+ Browse example registry items
+
+
+
+ Schema
- Registry item examples and configurations
+ Schema specification for registry.json
-
- Schema
-
- Schema specification for registry.json
-
-
diff --git a/apps/v4/content/docs/registry/meta.json b/apps/v4/content/docs/registry/meta.json
index 4905cedbc9..b82573fdc8 100644
--- a/apps/v4/content/docs/registry/meta.json
+++ b/apps/v4/content/docs/registry/meta.json
@@ -3,6 +3,7 @@
"pages": [
"index",
"getting-started",
+ "github",
"registry-index",
"examples",
"namespace",
diff --git a/apps/v4/content/docs/registry/namespace.mdx b/apps/v4/content/docs/registry/namespace.mdx
index f7c8b50322..efa16ce98d 100644
--- a/apps/v4/content/docs/registry/namespace.mdx
+++ b/apps/v4/content/docs/registry/namespace.mdx
@@ -156,6 +156,28 @@ The pattern for referencing resources is: `@namespace/resource-name`
---
+## GitHub and Namespaces
+
+GitHub registry addresses and namespaces solve different problems.
+
+Use a GitHub address when the registry is a public GitHub repository and you
+want users to install without configuring `components.json`.
+
+```bash
+npx shadcn@latest add acme/ui/button
+```
+
+Use a namespace when you want a stable alias, custom hosting, authentication,
+request headers, query parameters or private registry support.
+
+```bash
+npx shadcn@latest add @acme/button
+```
+
+See the [GitHub registry](/docs/registry/github) docs for more information.
+
+---
+
## Configuration
Namespaced registries are configured in your `components.json` file under the `registries` field.
diff --git a/apps/v4/content/docs/registry/registry-index.mdx b/apps/v4/content/docs/registry/registry-index.mdx
index 658127682b..180ddc2077 100644
--- a/apps/v4/content/docs/registry/registry-index.mdx
+++ b/apps/v4/content/docs/registry/registry-index.mdx
@@ -9,6 +9,10 @@ When you run `shadcn add` or `shadcn search`, the CLI will automatically check t
You can see the full list at [https://ui.shadcn.com/r/registries.json](https://ui.shadcn.com/r/registries.json).
+You do not need to submit a public GitHub registry to the registry directory to
+use it with `owner/repo/item` addresses. The registry directory is for
+namespaces such as `@acme`.
+
## Adding a Registry
1. Add your registry to [`apps/v4/registry/directory.json`](https://github.com/shadcn-ui/ui/blob/main/apps/v4/registry/directory.json)
diff --git a/apps/v4/content/docs/registry/registry-item-json.mdx b/apps/v4/content/docs/registry/registry-item-json.mdx
index f7badeee02..3f6405e290 100644
--- a/apps/v4/content/docs/registry/registry-item-json.mdx
+++ b/apps/v4/content/docs/registry/registry-item-json.mdx
@@ -161,23 +161,34 @@ Use `@version` to specify the version of the package.
### registryDependencies
-Used for registry dependencies. Can be names, namespaced or URLs.
+Used for registry dependencies. Each entry is an item address.
- 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 namespaced registry items, use `@namespace/item-name` eg. `['@acme/input-form']`.
+- For GitHub registry items, use `owner/repo/item-name` eg. `['acme/ui/button']`. For published registries, prefer a tag or full commit SHA eg. `['acme/ui/button#v1.2.0']`.
- For custom registry items use the URL of the registry item eg. `['https://example.com/r/hello-world.json']`.
+- For local registry item files use a file path eg. `['./hello-world.json']`.
```json title="registry-item.json" showLineNumbers
{
"registryDependencies": [
"button",
"@acme/input-form",
- "https://example.com/r/editor.json"
+ "acme/ui/button#v1.2.0",
+ "https://example.com/r/editor.json",
+ "./editor.json"
]
}
```
-Note: The CLI will automatically resolve remote registry dependencies.
+Note: Bare names keep their existing behavior. `button` means the built-in
+shadcn `button` item, not an item from the same GitHub repository. For
+same-repository GitHub dependencies, use the full GitHub item address.
+
+Refs are not inherited across dependencies. If a GitHub dependency should be
+reproducible, pin that dependency to its own tag or full commit SHA.
+
+See the [GitHub registry](/docs/registry/github) docs for more information.
### files
diff --git a/apps/v4/content/docs/registry/registry-json.mdx b/apps/v4/content/docs/registry/registry-json.mdx
index 710df0d983..27210ae9c3 100644
--- a/apps/v4/content/docs/registry/registry-json.mdx
+++ b/apps/v4/content/docs/registry/registry-json.mdx
@@ -49,6 +49,11 @@ using `include`.
}
```
+Public GitHub repositories use the same source registry format. The CLI reads
+the root `registry.json`, resolves `include`, and installs files from the
+repository. See the [GitHub registry](/docs/registry/github) docs for more
+information.
+
## Definitions
You can see the JSON Schema for `registry.json` [here](https://ui.shadcn.com/schema/registry.json).
diff --git a/apps/v4/lib/docs.ts b/apps/v4/lib/docs.ts
index 5c017f50c4..24868d64e5 100644
--- a/apps/v4/lib/docs.ts
+++ b/apps/v4/lib/docs.ts
@@ -1,8 +1,3 @@
-export const PAGES_NEW = [
- "/create",
- "/docs/registry",
- "/docs/registry/getting-started",
- "/docs/changelog",
-]
+export const PAGES_NEW = ["/create", "/docs/registry/github", "/docs/changelog"]
export const PAGES_UPDATED = ["/docs/components/button"]
diff --git a/packages/shadcn/src/commands/add.ts b/packages/shadcn/src/commands/add.ts
index 5cd2511d68..76e3561808 100644
--- a/packages/shadcn/src/commands/add.ts
+++ b/packages/shadcn/src/commands/add.ts
@@ -46,7 +46,7 @@ export const addOptionsSchema = z.object({
export const add = new Command()
.name("add")
.description("add a component to your project")
- .argument("[components...]", "names, url or local path to component")
+ .argument("[components...]", "item addresses to add")
.option("-y, --yes", "skip confirmation prompt.", false)
.option("-o, --overwrite", "overwrite existing files.", false)
.option(
diff --git a/packages/shadcn/src/commands/registry/validate.test.ts b/packages/shadcn/src/commands/registry/validate.test.ts
index 4ec685eb6f..5e8acd5b00 100644
--- a/packages/shadcn/src/commands/registry/validate.test.ts
+++ b/packages/shadcn/src/commands/registry/validate.test.ts
@@ -1,6 +1,7 @@
import * as fs from "fs/promises"
import { tmpdir } from "os"
import * as path from "path"
+import { validateGitHubRegistrySource } from "@/src/registry/github"
import { logger } from "@/src/utils/logger"
import { spinner } from "@/src/utils/spinner"
import { beforeEach, describe, expect, it, vi } from "vitest"
@@ -37,6 +38,17 @@ vi.mock("@/src/utils/spinner", () => ({
})),
}))
+vi.mock("@/src/registry/github", () => ({
+ validateGitHubRegistrySource: vi.fn(async () => ({
+ valid: true,
+ cwd: "acme/ui#HEAD",
+ registryFiles: 1,
+ registryFilePaths: ["acme/ui#HEAD/registry.json"],
+ items: 2,
+ diagnostics: [],
+ })),
+}))
+
describe("registry validate command", () => {
beforeEach(() => {
vi.clearAllMocks()
@@ -120,6 +132,47 @@ describe("registry validate command", () => {
])
expect(process.exitCode).toBe(1)
})
+
+ it("validates a GitHub source registry", async () => {
+ await validate.parseAsync(["acme/ui"], {
+ from: "user",
+ })
+
+ expect(validateGitHubRegistrySource).toHaveBeenCalledWith({
+ owner: "acme",
+ repo: "ui",
+ ref: undefined,
+ })
+ const validationSpinner = vi.mocked(spinner).mock.results[0].value
+ const summarySpinner = vi.mocked(spinner).mock.results[1].value
+ expect(validationSpinner.succeed).toHaveBeenCalledWith("Registry is valid.")
+ expect(spinner).toHaveBeenCalledWith("Checked 1 registry file and 2 items.")
+ expect(summarySpinner.succeed).toHaveBeenCalled()
+ expect(logger.log).toHaveBeenCalledWith(" - registry.json")
+ expect(process.exitCode).toBeUndefined()
+ })
+
+ it("does not treat an existing local path as a GitHub source registry", async () => {
+ const cwd = await createFixture({
+ "acme/ui": JSON.stringify({
+ name: "example",
+ homepage: "https://example.com",
+ items: [],
+ }),
+ })
+
+ await validate.parseAsync(["acme/ui", "--cwd", cwd], {
+ from: "user",
+ })
+
+ expect(validateGitHubRegistrySource).not.toHaveBeenCalled()
+ const validationSpinner = vi.mocked(spinner).mock.results[0].value
+ expect(validationSpinner.fail).toHaveBeenCalledWith(
+ "Registry validation failed."
+ )
+ expect(logger.log).toHaveBeenCalledWith(" - acme/ui")
+ expect(process.exitCode).toBe(1)
+ })
})
async function createFixture(files: Record