- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Data Table
- Date Picker
- Dialog
- Direction
- Drawer
- Dropdown Menu
- Empty
- Field
- Hover Card
- Input
- Input Group
- Input OTP
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Message Scroller
- Native Select
- Navigation Menu
- Pagination
- Popover
- Progress
- Radio Group
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Toast
- Toggle
- Toggle Group
- Tooltip
- Typography
The shadcn package exposes a set of programmatic APIs in addition to the CLI.
You can use these to fetch, resolve, and install registry items, validate
registry JSON, and build custom tooling on top of the registry.
Each API is available under a dedicated subpath import.
import { getRegistryItems } from "shadcn/registry"
import { registryItemSchema } from "shadcn/schema"The CLI commands themselves are not part of the public API. Only the imports documented below are considered stable.
shadcn/registry#
Fetch and resolve items from configured registries.
Most functions accept an options object. The two options below are common to all
of them. In the examples that follow, config refers to this optional value —
omit it to use the built-in registries.
config#
- Type:
Partial<Config> - Default: built-in registries only
The registry configuration to use. Its registries field maps a namespace
(e.g. @acme) to a URL and any authentication headers or environment
variables required to reach it. Use
getRegistriesConfig to load it from your project.
import { getRegistryItems } from "shadcn/registry"
const items = await getRegistryItems(["@acme/login-form"], {
config: {
registries: {
"@acme": "https://acme.com/r/{name}.json",
},
},
})useCache#
- Type:
boolean - Default:
true
Registry responses are cached in memory for the lifetime of the process, keyed by the resolved URL. Because the in-flight promise is cached, concurrent requests for the same URL are de-duplicated into a single fetch.
Leave this enabled for one-off scripts and CLI runs. Set it to false in
long-running processes (servers, watchers, the MCP server) where the registry
can change between requests and you need fresh data each time.
const fresh = await getRegistry("@shadcn", { useCache: false })getRegistriesConfig#
Load registry configuration from a project directory. The function reads
components.json when present; otherwise it reads the top-level registries
field from package.json.
import { getRegistriesConfig } from "shadcn/registry"
const config = await getRegistriesConfig(process.cwd())getRegistry#
Fetch a single registry by name.
import { getRegistry } from "shadcn/registry"
const registry = await getRegistry("@acme", {
config, // optional Partial<Config>
useCache: true,
})getRegistryItems#
Fetch one or more registry items by their qualified names.
import { getRegistryItems } from "shadcn/registry"
const items = await getRegistryItems(["@acme/button", "@acme/card"], {
config,
useCache: true,
})Returns an array of registry items:
[
{
"name": "button",
"type": "registry:ui",
"dependencies": ["@radix-ui/react-slot"],
"files": [
{
"path": "ui/button.tsx",
"type": "registry:ui",
"content": "..."
}
]
}
]resolveRegistryItems#
Resolve multiple items together with their registry dependencies, merged into a
single tree. Unlike getRegistryItems, which returns the
items as a list, this walks each item's registryDependencies and flattens
everything — files, dependencies, CSS variables — into one installable object.
import { resolveRegistryItems } from "shadcn/registry"
const tree = await resolveRegistryItems(
["@acme/button", "@acme/card", "@acme/dialog"],
{ config }
)Returns a single merged tree:
{
"dependencies": ["@radix-ui/react-slot", "@radix-ui/react-dialog"],
"files": [
{ "path": "ui/button.tsx", "type": "registry:ui", "content": "..." },
{ "path": "ui/card.tsx", "type": "registry:ui", "content": "..." },
{ "path": "ui/dialog.tsx", "type": "registry:ui", "content": "..." }
],
"cssVars": {
"theme": {
"font-heading": "Poppins, sans-serif"
},
"light": {
"brand": "oklch(0.205 0.015 18)"
},
"dark": {
"brand": "oklch(0.205 0.015 18)"
}
},
"docs": ""
}addRegistryItems#
Resolve and install registry items into an existing project. This is the
programmatic equivalent of shadcn add and applies files, package dependencies,
environment variables, CSS, and Tailwind configuration declared by the items.
import { addRegistryItems, getRegistriesConfig } from "shadcn/registry"
const cwd = process.cwd()
const config = await getRegistriesConfig(cwd)
await addRegistryItems(["@acme/agent"], {
cwd,
config,
overwrite: false,
silent: true,
})addRegistryItems does not read project configuration files itself. Pass the
result of getRegistriesConfig, or provide config
directly. A config containing only registries is enough when every requested
item and dependency is universal: a registry:item or registry:file whose
files all declare explicit targets. Other items require a full resolved project
config, including its aliases and resolvedPaths. Every custom registry
namespace referenced by an item or dependency must be present in config.
The function throws errors instead of exiting the process and never prompts.
Existing files are skipped unless overwrite is enabled, and npm uses
--force for React 19 peer dependency conflicts.
getRegistries#
Fetch the registry directory.
import { getRegistries } from "shadcn/registry"
const registries = await getRegistries({ useCache: true })Returns an array of registry entries:
[
{
"name": "@shadcn",
"url": "https://ui.shadcn.com/r/{name}.json",
"homepage": "https://ui.shadcn.com"
}
]searchRegistries#
Search across one or more registries with fuzzy matching.
import { searchRegistries } from "shadcn/registry"
const results = await searchRegistries(["@shadcn"], {
query: "button",
types: ["registry:component"],
limit: 100,
offset: 0,
config,
continueOnError: true, // skip (don't throw on) registries that fail to load
})Returns matching items wrapped in pagination metadata:
{
"pagination": { "total": 1, "offset": 0, "limit": 100, "hasMore": false },
"items": [
{
"name": "button",
"type": "registry:ui",
"description": "A button component.",
"registry": "@shadcn",
"addCommandArgument": "@shadcn/button"
}
]
}loadRegistry#
Read and resolve a local registry.json file from disk, following any
include references, and return the registry catalog.
import { loadRegistry } from "shadcn/registry"
const catalog = await loadRegistry({
cwd: process.cwd(), // defaults to process.cwd()
registryFile: "registry.json", // defaults to "registry.json"
})The returned catalog lists every item but omits file contents — like a
built registry.json index.
getRegistry fetches a remote registry over the network
(by namespace, URL or GitHub address) and expects the served catalog to
already be flattened — it rejects catalogs that still use include.
loadRegistry reads a local registry.json from disk and resolves
include references itself.
loadRegistryItem#
Read a single item from a local registry.json by name, with its file contents
read from disk and inlined.
import { loadRegistryItem } from "shadcn/registry"
const item = await loadRegistryItem("login-form", { cwd: process.cwd() })Returns a fully resolved registry item with file contents:
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "login-form",
"type": "registry:component",
"files": [
{
"path": "registry/new-york/login-form.tsx",
"type": "registry:component",
"content": "..."
}
]
}getRegistryItems resolves items from a remote
registry over the network. loadRegistryItem builds a single item on demand
from your local source files, reading each file from disk. Use it in a
dynamic route that serves registry-item.json responses.
Errors#
All registry functions throw typed errors that extend RegistryError. Use the
RegistryErrorCode enum or instanceof checks to handle them.
import { RegistryError, RegistryNotFoundError } from "shadcn/registry"
try {
await getRegistry("@unknown")
} catch (error) {
if (error instanceof RegistryNotFoundError) {
// handle missing registry
}
}Available error classes:
RegistryErrorRegistryNotFoundErrorRegistryUnauthorizedErrorRegistryForbiddenErrorRegistryFetchErrorRegistryNotConfiguredErrorRegistryLocalFileErrorRegistryParseErrorRegistryValidationErrorRegistryItemNotFoundErrorRegistriesIndexParseErrorRegistryMissingEnvironmentVariablesErrorRegistryInvalidNamespaceError
shadcn/schema#
The Zod schemas used to validate registry.json, registry-item.json and
components.json. Useful for validating registry data in your own tooling.
import { registryItemSchema, registrySchema } from "shadcn/schema"
const result = registryItemSchema.safeParse(json)
if (!result.success) {
console.error(result.error)
}Key schemas:
registrySchemaregistryItemSchemaregistryItemFileSchemaregistryItemTypeSchemaregistryItemCssVarsSchemaregistryItemTailwindSchemaregistryBaseColorSchemaconfigSchemapresetSchema
Inferred types are exported alongside them:
RegistryRegistryItemRegistryBaseItemRegistryFontItemPresetConfigJson
shadcn/preset#
Encode, decode and validate theme presets, plus the preset option constants used by the theme editor.
encodePreset#
Encode a Partial<PresetConfig> into a short, URL-safe preset code. Any fields
you omit fall back to DEFAULT_PRESET_CONFIG.
import { encodePreset } from "shadcn/preset"
const code = encodePreset({
style: "radix-force-ui",
baseColor: "stone",
theme: "blue",
radius: "large",
font: "geist",
})Returns a version-prefixed string:
"bJ4FLU0"decodePreset#
Decode a preset code back into a full PresetConfig. Returns null if the code
is missing or invalid.
import { decodePreset } from "shadcn/preset"
const config = decodePreset("bJ4FLU0")Returns the resolved config (omitted fields are filled with their defaults):
{
"style": "radix-force-ui",
"baseColor": "stone",
"theme": "blue",
"chartColor": "neutral",
"iconLibrary": "lucide",
"font": "geist",
"fontHeading": "inherit",
"radius": "large",
"menuAccent": "subtle",
"menuColor": "default"
}decodePreset("not-a-code") // nullOther exports#
Additional functions for validating codes and generating random presets:
isPresetCodeisValidPresetgenerateRandomConfiggenerateRandomPresettoBase62fromBase62
Constants:
PRESET_BASESPRESET_STYLESPRESET_BASE_COLORSPRESET_THEMESPRESET_ICON_LIBRARIESPRESET_FONTSPRESET_FONT_HEADINGSPRESET_RADIIPRESET_MENU_ACCENTSPRESET_MENU_COLORSPRESET_CHART_COLORSDEFAULT_PRESET_CONFIG