Shadcn Components
Use this skill to add new components from the library or create custom components that align with Shadcn's architecture.
Documentation
Workflow
1. Prerequisites
- Verify Initialization: Check if
components.jsonexists in the project root. - If Missing: Stop and run the
shadcn-setupskill first. You cannot add components to an uninitialized project.
2. Add or Update Components
To install a component from the registry:
pnpm dlx shadcn@latest add [component-name]
- Overwrite: If the component exists and you are updating it, the CLI may ask to overwrite. Use
--overwriteflag if you are certain. - Multiple: You can add multiple components at once:
pnpm dlx shadcn@latest add button card input.
2. Create Custom Components (The "Shadcn Way")
When creating a new component that isn't in the registry, follow these patterns:
A. Structure
Start with a file in components/ui/ (or configured path).
B. Imports
MUST import the cn utility for class merging.
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
C. Variant Definitions with cva
Define visual variants using class-variance-authority.
const badgeVariants = cva(
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
}
)
D. Component Definition
- Export the interface extending HTML attributes.
- Include
classNameandvariantprops. - Use
cn()to mergeclassNameprop with variants. - Set
displayNamefor debugging if needed.
export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) {
return (
<div className={cn(badgeVariants({ variant }), className)} {...props} />
)
}
export { Badge, badgeVariants }
3. Anti-Patterns ❌
- Strings without
cn(): Never manually concat strings like`bg-red-500 ${className}`. Always usecn(). - Ignoring Props: Always spread
...propsto the underlying element to ensure accessibility attributes and event listeners pass through.