Comprehensive usage examples and agent integration patterns for shadcn.
For coding agents, the typical workflow for adding components programmatically:
import { resolveRegistryItems } from 'shadcn';
import { writeFile, mkdir } from 'fs/promises';
import { dirname, join } from 'path';
import { existsSync } from 'fs';
async function addComponent(componentName: string, cwd: string) {
try {
// 1. Resolve component with all dependencies
const resolved = await resolveRegistryItems([componentName], {
config: { /* project config */ }
});
// 2. Install npm dependencies (use package manager of choice)
if (resolved.dependencies && resolved.dependencies.length > 0) {
// Example: npm install ${resolved.dependencies.join(' ')}
// Or use child_process.exec to run package manager
}
// 3. Write component files
if (resolved.files) {
for (const file of resolved.files) {
const filePath = join(cwd, file.path);
const dir = dirname(filePath);
// Create directory if it doesn't exist
if (!existsSync(dir)) {
await mkdir(dir, { recursive: true });
}
// Write file content
await writeFile(filePath, file.content || '', 'utf-8');
}
}
// 4. Update Tailwind config if needed
if (resolved.tailwind?.config) {
// Merge resolved.tailwind.config into tailwind.config.js
// Use a JSON/JS parser to safely merge configurations
}
// 5. Update CSS if needed
if (resolved.css) {
// Append resolved.css to your CSS file
// Read existing CSS, append new CSS, write back
}
// 6. Set environment variables if needed
if (resolved.envVars) {
// Add to .env file
// Read existing .env, merge new vars, write back
}
// 7. Handle fonts if present
if (resolved.fonts) {
// Process font items (e.g., add to next/font configuration)
}
return { success: true, resolved };
} catch (error) {
// Handle errors appropriately
throw error;
}
}Discover and load configuration from components.json:
import { getRegistriesConfig } from 'shadcn';
import { findUp } from 'find-up';
import { dirname } from 'path';
async function discoverConfig(cwd: string = process.cwd()) {
// Find components.json in current or parent directories
const configPath = await findUp('components.json', { cwd });
if (!configPath) {
throw new Error('components.json not found. Run: npx shadcn init');
}
const configDir = dirname(configPath);
const { registries } = await getRegistriesConfig(configDir);
return { configPath, configDir, registries };
}Handle errors gracefully with recovery suggestions:
import {
getRegistryItems,
RegistryNotFoundError,
RegistryNotConfiguredError,
ConfigMissingError,
RegistryFetchError,
RegistryUnauthorizedError
} from 'shadcn/registry';
import { searchRegistries } from 'shadcn/registry';
async function safeGetRegistryItems(items: string[], cwd?: string) {
try {
return await getRegistryItems(items, { config: { /* ... */ } });
} catch (error) {
if (error instanceof ConfigMissingError) {
// Suggest initialization
throw new Error('Project not initialized. Run: npx shadcn init');
} else if (error instanceof RegistryNotConfiguredError) {
// Suggest registry configuration
throw new Error(`Registry not configured. Add to components.json: ${error.suggestion}`);
} else if (error instanceof RegistryNotFoundError) {
// Try to find similar items via search
const searchTerm = items[0].split('/').pop() || items[0];
const searchResults = await searchRegistries(['@shadcn'], {
query: searchTerm,
limit: 5
});
if (searchResults.items.length > 0) {
throw new Error(
`Item not found: ${items[0]}. Did you mean: ${searchResults.items.map(i => i.addCommandArgument).join(', ')}?`
);
}
throw new Error(`Item not found: ${items[0]}. Try: npx shadcn search ${searchTerm}`);
} else if (error instanceof RegistryUnauthorizedError) {
// Check for missing environment variables
throw new Error(
`Authentication required. Check registry configuration and ensure required environment variables are set.`
);
} else if (error instanceof RegistryFetchError) {
// Network error - suggest retry
throw new Error(`Network error: ${error.message}. Please check your connection and try again.`);
}
throw error;
}
}Detect and handle circular dependencies:
import { resolveRegistryItems } from 'shadcn';
import { RegistryError } from 'shadcn/registry';
async function safeResolveItems(items: string[]) {
try {
return await resolveRegistryItems(items);
} catch (error) {
if (error instanceof RegistryError && error.code === 'VALIDATION_ERROR') {
// Check if error message indicates circular dependency
if (error.message.includes('circular') || error.message.includes('dependency')) {
throw new Error(
`Circular dependency detected in registry items: ${items.join(', ')}. ` +
`This usually indicates a registry configuration issue.`
);
}
}
throw error;
}
}Process multiple items with partial failure handling:
import { getRegistryItems } from 'shadcn';
import { RegistryNotFoundError } from 'shadcn/registry';
async function batchGetItems(itemNames: string[]) {
const results: Array<{ name: string; item?: any; error?: string }> = [];
// Process items individually to handle partial failures
for (const itemName of itemNames) {
try {
const [item] = await getRegistryItems([itemName]);
results.push({ name: itemName, item });
} catch (error) {
if (error instanceof RegistryNotFoundError) {
results.push({ name: itemName, error: `Not found: ${error.message}` });
} else {
results.push({ name: itemName, error: `Error: ${error.message}` });
}
}
}
return results;
}Handle multiple components.json files in workspace:
import { getRegistriesConfig } from 'shadcn';
import { findUp } from 'find-up';
import { dirname } from 'path';
async function getWorkspaceConfigs(rootDir: string) {
// Find all components.json files in workspace
const configs: Array<{ path: string; config: any }> = [];
// Search for components.json in root and subdirectories
let currentDir = rootDir;
while (currentDir) {
const configPath = await findUp('components.json', { cwd: currentDir });
if (configPath) {
const configDir = dirname(configPath);
const { registries } = await getRegistriesConfig(configDir);
configs.push({ path: configPath, config: { registries } });
// Move to parent directory to find other configs
const parentDir = dirname(configDir);
if (parentDir === currentDir) break; // Reached filesystem root
currentDir = parentDir;
} else {
break;
}
}
return configs;
}Search for components and get suggestions:
import { searchRegistries } from 'shadcn/registry';
async function findComponents(query: string) {
const results = await searchRegistries(['@shadcn'], {
query,
limit: 10
});
return results.items.map(item => ({
name: item.name,
description: item.description,
type: item.type,
addCommand: `npx shadcn add ${item.addCommandArgument}`
}));
}Use CLI commands programmatically with proper exit code handling:
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);
async function addComponentCLI(componentName: string, cwd: string) {
try {
const { stdout, stderr } = await execAsync(
`npx shadcn add ${componentName} --yes --overwrite`,
{ cwd }
);
// Exit code 0 = success
// Exit code 1 = general error
// Exit code 2 = configuration error
// Exit code 3 = registry error
// Exit code 4 = validation error
return { success: true, stdout, stderr };
} catch (error: any) {
const exitCode = error.code;
switch (exitCode) {
case 2:
throw new Error('Configuration error. Run: npx shadcn init');
case 3:
throw new Error('Registry error. Check component name and network connection');
case 4:
throw new Error('Validation error. Check component name format');
default:
throw new Error(`Error: ${error.message}`);
}
}
}Use MCP tools for AI assistant workflows:
// MCP server provides 7 tools:
// - get_project_registries
// - list_items_in_registries
// - search_items_in_registries
// - view_items_in_registries
// - get_item_examples_from_registries
// - get_add_command_for_items
// - get_audit_checklist
// Start MCP server
// npx shadcn mcp
// Or use programmatically
import { server } from 'shadcn/mcp';
// Server is pre-configured and ready to useSee MCP Reference for complete MCP documentation.