Automated summary
This skill guides adding and customizing shadcn/ui components in the shared packages/ui UI package.
Strengths
- Describes an end-to-end workflow for searching, adding via the shadcn CLI, exporting, and using components in apps.
- Uses shadcn MCP tools to search components, inspect implementations, and get add commands.
- Provides concrete examples of exports and imports from the shared UI package.
- Also covers customizing variants and styling with Tailwind.
Limitations
- Assumes a specific monorepo layout with packages/ui and an existing components.json configuration.
- Does not address testing, advanced accessibility, or migration strategies for existing components.
- Relies on shadcn MCP tools and the shadcn CLI being available to work fully.
Use when you need to add, update, or customize shadcn/ui components in the project’s shared UI library.
Avoid when the project does not use shadcn/ui, does not follow the packages/ui structure, or the task is unrelated to shared UI components.
Security analysis
CautionThe skill only teaches how to search, add, and customize shadcn/ui components in a monorepo UI package. Its only powerful action is invoking the official shadcn CLI through Bash, which is expected for this task and is not obfuscated or destructive. The sole caution is the unpinned `@latest` npx execution, which pulls and runs remote code; version pinning would reduce supply-chain risk.
- •Instructs running `npx shadcn@latest add ...` via Bash, which downloads and executes an unpinned third-party package from the network — legitimate for this workflow but should ideally pin a version or verify the CLI before running.
- •No destructive filesystem operations, credential handling, or outbound data transfer beyond the shadcn registry/CLI.
Examples
Add a dropdown-menu component from the shadcn registry to packages/ui, export its parts from the UI package index, and show how to import and use it in apps/web.Update the existing shadcn/ui button component in packages/ui to add a new destructive-outline variant using Tailwind classes while preserving the existing variants.Search the shadcn registry for form input examples, inspect the relevant components, and recommend which ones to add to packages/ui for a settings form.name: shadcn-components description: Add or customize shadcn/ui components in the shared UI package. Use when adding new components from shadcn registry or updating existing component variants. allowed-tools: Read, Edit, Write, Bash, mcp__shadcn__search_items_in_registries, mcp__shadcn__view_items_in_registries, mcp__shadcn__get_add_command_for_items
shadcn/ui Components Skill
This skill helps you work with shadcn/ui components in packages/ui/.
When to Use This Skill
- Adding new shadcn/ui components to the shared UI library
- Customizing existing shadcn/ui component variants
- Updating component styling with Tailwind
- Finding and implementing component examples
- Debugging shadcn/ui component issues
- Managing component dependencies
shadcn/ui Overview
shadcn/ui is a collection of re-usable components built with Radix UI and Tailwind CSS. Components are copied into your codebase, giving you full control.
packages/ui/
├── src/
│ ├── components/ # shadcn/ui components
│ │ ├── badge.tsx
│ │ ├── button.tsx
│ │ ├── card.tsx
│ │ ├── dialog.tsx
│ │ └── ...
│ ├── lib/
│ │ └── utils.ts # cn() utility for class merging
│ └── styles/
│ └── globals.css # Global styles
├── components.json # shadcn/ui configuration
└── package.json
Discovery and Research
Search for Components
// Use MCP tool to search for components
mcp__shadcn__search_items_in_registries({
registries: ["@shadcn"],
query: "button"
})
mcp__shadcn__search_items_in_registries({
registries: ["@shadcn"],
query: "form input"
})
View Component Details
// Get component implementation details
mcp__shadcn__view_items_in_registries({
items: ["@shadcn/button", "@shadcn/card"]
})
Get Component Examples
// Find usage examples
mcp__shadcn__get_item_examples_from_registries({
registries: ["@shadcn"],
query: "button demo"
})
mcp__shadcn__get_item_examples_from_registries({
registries: ["@shadcn"],
query: "form example"
})
Get Add Command
// Get CLI command to add components
mcp__shadcn__get_add_command_for_items({
items: ["@shadcn/button", "@shadcn/dialog"]
})
Adding New Components
Step 1: Search and Research
# Use MCP tools to find the component
# Example: Adding a dropdown menu component
mcp__shadcn__search_items_in_registries({
registries: ["@shadcn"],
query: "dropdown menu"
})
Step 2: Get Add Command
mcp__shadcn__get_add_command_for_items({
items: ["@shadcn/dropdown-menu"]
})
// Returns: npx shadcn@latest add dropdown-menu
Step 3: Add Component
# Navigate to packages/ui
cd packages/ui
# Add component using CLI
npx shadcn@latest add dropdown-menu
# Or add multiple components at once
npx shadcn@latest add dropdown-menu select tabs
Step 4: Export Component
Update packages/ui/src/index.ts:
export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
} from "./components/dropdown-menu";
Step 5: Use in App
// In apps/web or apps/api
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
} from "@sgcarstrends/ui";
export function UserMenu() {
return (
<DropdownMenu>
<DropdownMenuTrigger>Open</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>Profile</DropdownMenuItem>
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuItem>Logout</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
Core Components
Button Component
// packages/ui/src/components/button.tsx
import { Button } from "@sgcarstrends/ui";
// Variants
<Button variant="default">Default</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="outline">Outline</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
// Sizes
<Button size="default">Default</Button>
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>
<Button size="icon">Icon</Button>
Card Component
import {
Card,
CardHeader,
CardFooter,
CardTitle,
CardDescription,
CardContent,
} from "@sgcarstrends/ui";
export function InfoCard() {
return (
<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
<CardDescription>Card description goes here</CardDescription>
</CardHeader>
<CardContent>
<p>Card content</p>
</CardContent>
<CardFooter>
<p>Card footer</p>
</CardFooter>
</Card>
);
}
Dialog Component
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@sgcarstrends/ui";
export function ConfirmDialog() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Open Dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you sure?</DialogTitle>
<DialogDescription>
This action cannot be undone.
</DialogDescription>
</DialogHeader>
<div className="flex justify-end gap-2">
<Button variant="outline">Cancel</Button>
<Button>Confirm</Button>
</div>
</DialogContent>
</Dialog>
);
}
Form Components
import { Label } from "@sgcarstrends/ui";
import { Input } from "@sgcarstrends/ui";
import { Textarea } from "@sgcarstrends/ui";
export function ContactForm() {
return (
<form className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name">Name</Label>
<Input id="name" placeholder="Enter your name" />
</div>
<div className="space-y-2">
<Label htmlFor="message">Message</Label>
<Textarea id="message" placeholder="Enter your message" />
</div>
<Button type="submit">Submit</Button>
</form>
);
}
Badge Component
import { Badge } from "@sgcarstrends/ui";
export function StatusBadge({ status }: { status: string }) {
return (
<>
<Badge variant="default">Default</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="destructive">Destructive</Badge>
<Badge variant="outline">Outline</Badge>
</>
);
}
Customization
Customizing Component Variants
Edit component file directly:
// packages/ui/src/components/button.tsx
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
// Add custom variant
success: "bg-green-500 text-white hover:bg-green-600",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
// Add custom size
xl: "h-14 rounded-md px-10 text-lg",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
Creating Compound Components
// packages/ui/src/components/stat-card.tsx
import { Card, CardHeader, CardTitle, CardContent } from "./card";
import { cn } from "../lib/utils";
interface StatCardProps {
title: string;
value: string | number;
description?: string;
trend?: "up" | "down";
className?: string;
}
export function StatCard({
title,
value,
description,
trend,
className,
}: StatCardProps) {
return (
<Card className={cn("", className)}>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">{title}</CardTitle>
{trend && (
<span
className={cn(
"text-xs",
trend === "up" ? "text-green-500" : "text-red-500"
)}
>
{trend === "up" ? "↑" : "↓"}
</span>
)}
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{value}</div>
{description && (
<p className="text-xs text-muted-foreground">{description}</p>
)}
</CardContent>
</Card>
);
}
Export in packages/ui/src/index.ts:
export { StatCard } from "./components/stat-card";
Theming
Customizing Colors
Edit packages/ui/src/styles/globals.css:
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
}
}
Component Configuration
components.json
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/styles/globals.css",
"baseColor": "slate",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
Utility Functions
cn() Utility
// packages/ui/src/lib/utils.ts
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Usage:
import { cn } from "@sgcarstrends/ui/lib/utils";
export function MyComponent({ className }: { className?: string }) {
return (
<div className={cn("base-classes", "conditional-classes", className)}>
Content
</div>
);
}
Testing Components
// packages/ui/src/components/__tests__/button.test.tsx
import { render, screen } from "@testing-library/react";
import { Button } from "../button";
describe("Button", () => {
it("renders correctly", () => {
render(<Button>Click me</Button>);
expect(screen.getByText("Click me")).toBeInTheDocument();
});
it("applies variant styles", () => {
render(<Button variant="destructive">Delete</Button>);
const button = screen.getByText("Delete");
expect(button).toHaveClass("bg-destructive");
});
it("handles click events", () => {
const handleClick = vi.fn();
render(<Button onClick={handleClick}>Click me</Button>);
screen.getByText("Click me").click();
expect(handleClick).toHaveBeenCalledOnce();
});
});
Run tests:
pnpm -F @sgcarstrends/ui test
Common Patterns
Responsive Components
import { Button } from "@sgcarstrends/ui";
export function ResponsiveButton() {
return (
<Button className="w-full md:w-auto">
Responsive Button
</Button>
);
}
Composition
import { Card, CardHeader, CardTitle, CardContent } from "@sgcarstrends/ui";
import { Button } from "@sgcarstrends/ui";
export function ActionCard({ title, children, onAction }: Props) {
return (
<Card>
<CardHeader>
<CardTitle>{title}</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{children}
<Button onClick={onAction} className="w-full">
Take Action
</Button>
</CardContent>
</Card>
);
}
Troubleshooting
Component Not Found
Problem: Import error when using component Solution:
- Check component is exported in
packages/ui/src/index.ts - Verify component file exists in
packages/ui/src/components/ - Run
pnpm installto update dependencies
Styling Issues
Problem: Tailwind classes not applying Solution:
- Check
packages/ui/tailwind.config.tsincludes correct content paths - Verify CSS variables are defined in
globals.css - Ensure parent app imports UI package's global CSS
TypeScript Errors
Problem: Type errors when using components Solution:
- Check component props are properly typed
- Run
pnpm buildin packages/ui - Restart TypeScript server in IDE
Updating Components
When shadcn/ui updates:
cd packages/ui
# Update specific component
npx shadcn@latest add button --overwrite
# Update multiple components
npx shadcn@latest add button card dialog --overwrite
References
- shadcn/ui Documentation: https://ui.shadcn.com
- Radix UI: https://www.radix-ui.com
- Related files:
packages/ui/src/components/- All UI componentspackages/ui/components.json- shadcn/ui configpackages/ui/CLAUDE.md- UI package documentation
Best Practices
- Use MCP Tools: Search before adding to avoid duplicates
- Export Components: Always export in index.ts
- Naming: Follow shadcn/ui naming conventions
- Testing: Write tests for custom variants
- Documentation: Document custom components
- Versioning: Keep shadcn/ui components updated
- Customization: Extend, don't modify core components
- Type Safety: Leverage TypeScript for props
- Size Utility: Use
size-*instead ofh-* w-*for equal dimensions (Tailwind v3.4+)
Size Utility Convention
When styling shadcn/ui components with equal height and width, use the size-* utility:
// ✅ Good - Use size-* for equal dimensions
<Button size="icon" className="size-10">
<Icon className="size-4" />
</Button>
<Avatar className="size-8">
<AvatarImage src={imageUrl} />
</Avatar>
// ❌ Avoid - Redundant h-* and w-*
<Button size="icon" className="h-10 w-10">
<Icon className="h-4 w-4" />
</Button>
<Avatar className="h-8 w-8">
<AvatarImage src={imageUrl} />
</Avatar>
From the file to a first result
The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.
View the source and supporting filesThis download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.
1. Download the file
Download SKILL.mdFrom the root of a test project, save the file as .skill-downloads/shadcn-ui-components-mmbizk7f/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.
Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.
mkdir -p '.skill-downloads/shadcn-ui-components-mmbizk7f' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/shadcn-ui-components-mmbizk7f/download' --output '.skill-downloads/shadcn-ui-components-mmbizk7f/SKILL.md.part' && mv '.skill-downloads/shadcn-ui-components-mmbizk7f/SKILL.md.part' '.skill-downloads/shadcn-ui-components-mmbizk7f/SKILL.md'2. Check prerequisites
Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.
Tools declared by the author (check availability in your agent): Read, Edit, Write, Bash, mcp__shadcn__search_items_in_registries, mcp__shadcn__view_items_in_registries, mcp__shadcn__get_add_command_for_items
No recognized prerequisites section in this file. This does not mean it is self-contained: libraries, tools and access requirements may be mentioned elsewhere in the content or source repository.
The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.
3. Load the skill
After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.
Claude Code
test -f '.skill-downloads/shadcn-ui-components-mmbizk7f/SKILL.md' && test -s '.skill-downloads/shadcn-ui-components-mmbizk7f/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/shadcn-components' && cp -R '.skill-downloads/shadcn-ui-components-mmbizk7f' '.claude/skills/shadcn-components'OpenSkills
OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.
Official OpenSkills installationopenskills install './.skill-downloads/shadcn-ui-components-mmbizk7f'openskills read 'shadcn-ui-components-mmbizk7f'Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.
For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.
openskills syncManual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.
4. Try a first task
Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:
Read .skill-downloads/shadcn-ui-components-mmbizk7f/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.
React Hook Form Best Practices
Development
Master React Hook Form: avoid anti-patterns, manage subscriptions, and handle reset, dirty state, and controlled inputs correctly.
Emotion to Mantine Migration
Development
Migrate Emotion styled-components to Mantine components with style props and CSS modules. Use when converting .styled.tsx files or removing @emotion imports.
Scope Creep Detector
Development
Analyzes git diffs against a stated intent to detect scope creep, unrelated files, broad PRs, and oversized changes, then suggests keep, split, or justify actions.