Composants shadcn/ui

VérifiéPrudence

Pour ajouter ou personnaliser des composants shadcn/ui dans un package UI partagé. Aide à ajouter de nouveaux composants depuis le registre shadcn, mettre à jour des variantes existantes ou gérer les dépendances de composants. Inclut des outils MCP pour rechercher, consulter et ajouter des composants via CLI.

Spar Skills Guide Bot
DeveloppementIntermédiaire
04/03/2026
Claude Code
#shadcn-ui#ui-components#tailwind-css#radix-ui#monorepo

Recommandé pour

Synthèse automatique

Cette compétence guide l’ajout et la personnalisation de composants shadcn/ui dans le package UI partagé packages/ui.

Points forts

  • Décrit un flux complet de recherche, d’ajout via la CLI shadcn, d’export et d’utilisation dans les applications.
  • S’appuie sur les outils MCP shadcn pour rechercher des composants, consulter leur implémentation et obtenir les commandes d’ajout.
  • Fournit des exemples concrets d’exports et d’imports depuis le package UI partagé.
  • Couvre aussi la personnalisation des variantes et le style avec Tailwind.

Limites

  • Suppose une organisation de monorepo précise avec packages/ui et un fichier components.json déjà configuré.
  • Ne traite pas des tests, de l’accessibilité avancée ou des stratégies de migration de composants existants.
  • Dépend de la disponibilité des outils MCP shadcn et de la CLI shadcn pour fonctionner pleinement.
Quand l'utiliser

À utiliser lorsqu’il faut ajouter, mettre à jour ou personnaliser des composants shadcn/ui dans la bibliothèque UI partagée du projet.

Quand l'éviter

À éviter si le projet n’utilise pas shadcn/ui, ne suit pas la structure packages/ui, ou si l’intervention ne concerne pas les composants UI partagés.

Analyse de sécurité

Prudence
Score qualité76/100

The 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.

Points d'attention
  • •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.
Comment cette analyse est faite

Exemples

Add a dropdown menu
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.
Customize button variants
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.
Find form input examples
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:

  1. Check component is exported in packages/ui/src/index.ts
  2. Verify component file exists in packages/ui/src/components/
  3. Run pnpm install to update dependencies

Styling Issues

Problem: Tailwind classes not applying Solution:

  1. Check packages/ui/tailwind.config.ts includes correct content paths
  2. Verify CSS variables are defined in globals.css
  3. Ensure parent app imports UI package's global CSS

TypeScript Errors

Problem: Type errors when using components Solution:

  1. Check component props are properly typed
  2. Run pnpm build in packages/ui
  3. 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 components
    • packages/ui/components.json - shadcn/ui config
    • packages/ui/CLAUDE.md - UI package documentation

Best Practices

  1. Use MCP Tools: Search before adding to avoid duplicates
  2. Export Components: Always export in index.ts
  3. Naming: Follow shadcn/ui naming conventions
  4. Testing: Write tests for custom variants
  5. Documentation: Document custom components
  6. Versioning: Keep shadcn/ui components updated
  7. Customization: Extend, don't modify core components
  8. Type Safety: Leverage TypeScript for props
  9. Size Utility: Use size-* instead of h-* 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>

Du fichier au premier essai

Le téléchargement contient exactement le contenu affiché sur cette fiche. Cette copie peut différer de la dernière version du dépôt source.

Consulter la source et ses fichiers annexes

Ce téléchargement contient uniquement SKILL.md. Les scripts, références, modèles et dépendances éventuels ne sont pas inclus. Récupérez les fichiers nécessaires depuis la source en conservant leur arborescence avant de poursuivre.

1. Télécharger le fichier

Télécharger SKILL.md

Depuis la racine d’un projet de test, enregistrez le fichier sous .skill-downloads/shadcn-ui-components-mmbizk7f/SKILL.md, ou utilisez la commande suivante. Ce dossier sert à examiner la skill avant son installation.

Commande pour macOS/Linux ou WSL : un shell POSIX et curl sont nécessaires. Sous Windows sans WSL, utilisez le bouton de téléchargement et créez le dossier manuellement.

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. Vérifier les prérequis

Lisez SKILL.md avant de l’utiliser. Si le fichier cite des scripts ou services externes, examinez-les et préparez les accès nécessaires dans votre environnement de test.

Outils déclarés par l’auteur (à vérifier dans votre agent) : Read, Edit, Write, Bash, mcp__shadcn__search_items_in_registries, mcp__shadcn__view_items_in_registries, mcp__shadcn__get_add_command_for_items

Aucune section de prérequis reconnue dans ce fichier. Cela ne signifie pas qu’il est autonome : les bibliothèques, outils et accès peuvent être cités ailleurs dans le contenu ou dans le dépôt source.

Les plateformes de la fiche sont des indications du catalogue. Les chemins ci-dessous suivent la documentation des outils ; ils ne prouvent pas que cette skill y a été exécutée. Aucune version minimale de ces outils n’est attestée pour cette fiche.

3. Charger la skill

Après vérification, copiez le dossier complet dans le projet avec l’une des commandes ci-dessous, puis ouvrez une nouvelle session de l’outil. La commande s’arrête si un dossier du même nom existe déjà : comparez les fichiers avant de le remplacer.

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 doit déjà être installé. La procédure locale install/read est contrôlée avec OpenSkills 1.5.0, qui exige Node.js ≥ 20.6.0. Aucun slug du catalogue n’est résolu automatiquement.

Installation officielle d’OpenSkills
openskills install './.skill-downloads/shadcn-ui-components-mmbizk7f'

openskills read 'shadcn-ui-components-mmbizk7f'

La lecture doit afficher le contenu du fichier installé et son dossier de base. Elle ne lance ni la skill ni ses dépendances.

Pour un agent qui lit AGENTS.md, synchronisez ensuite l’index. Cette commande modifie AGENTS.md : relisez le diff avant de l’utiliser.

openskills sync

Mode manuel ou autre outil : joignez SKILL.md à une conversation, ou ouvrez-le dans un agent capable de lire les fichiers. Ajoutez les ressources annexes nécessaires. Il n’existe pas de dossier universel reconnu par tous les outils.

4. Faire un premier essai

Utilisez une entrée courte et fictive : un endpoint d’API, un petit document ou un brief d’interface. Dans une nouvelle conversation, joignez le fichier ou donnez son chemin à l’agent, puis adaptez cette demande :

Lis .skill-downloads/shadcn-ui-components-mmbizk7f/SKILL.md et applique ses instructions à cet exemple : [décrire une entrée de test]. Indique les fichiers, dépendances ou accès qui manquent. Produis le résultat demandé par la skill sans inventer de données ni exécuter de script non examiné.

Résultat attendu : une sortie correspondant aux instructions de la skill, ou une liste précise des prérequis manquants. Une simple confirmation de lecture ne valide pas le premier usage. Si une ressource externe est indisponible, ce parcours reste à tester.

Skills similaires