BaliseTonSite

shadcn/ui

Des composants pro, prêts à l'emploi, que tu possèdes.

10.1C'est quoi shadcn/ui ?

shadcn/ui n'est pas une librairie de composants classique. C'est une collection de composants que tu copies dans ton projet. Chaque composant est :

Lisible

Du Tailwind CSS que tu connais déjà. Pas de magie.

Modifiable

Le code est chez toi. Tu changes ce que tu veux.

Accessible

Basé sur Radix UI : clavier, lecteur d'écran, ARIA.

10.2Installation

Dans un projet Next.js avec Tailwind déjà configuré :

Bashbash
# Initialiser shadcn/ui dans ton projet
npx shadcn@latest init

# Il va te poser quelques questions :
# ✔ Style → Default (ou New York)
# ✔ Base color → Slate
# ✔ CSS variables → Yes

L'init crée deux fichiers importants :

JSONjson
// components.json - La config shadcn
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "default",
  "tailwind": {
    "config": "tailwind.config.ts",
    "css": "app/globals.css",
    "baseColor": "slate"
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils"
  }
}
TypeScripttypescript
// lib/utils.ts - La fameuse fonction cn()
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

// Tu la connais déjà du chapitre 8 !

10.3Ajouter des composants

Chaque composant s'ajoute à la demande avec la CLI :

Bashbash
# Ajouter un composant
npx shadcn@latest add button
npx shadcn@latest add card
npx shadcn@latest add input
npx shadcn@latest add dialog

# Ajouter plusieurs d'un coup
npx shadcn@latest add button card input label

# Voir tous les composants disponibles
npx shadcn@latest add

Quand tu ajoutes un Button, voici ce qui se passe :

Bashbash
components/
└── ui/
    └── button.tsx Copié dans TON projet
                        Ce n'est pas un import npm.
                        Tu peux le modifier librement.

Et voici le code généré, du Tailwind pur que tu reconnais :

TSXtsx
// components/ui/button.tsx (simplifié)
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"

const buttonVariants = cva(
  // Classes de base communes à tous les boutons
  "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
  {
    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",
      },
      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",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, ...props }, ref) => (
    <button
      className={cn(buttonVariants({ variant, size, className }))}
      ref={ref}
      {...props}
    />
  )
)
Button.displayName = "Button"

export { Button, buttonVariants }

10.4Utilisation au quotidien

Une fois les composants ajoutés, l'utilisation est simple et élégante :

TSXtsx
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"

export function LoginForm() {
  return (
    <Card className="w-[400px]">
      <CardHeader>
        <CardTitle>Connexion</CardTitle>
        <CardDescription>Entre tes identifiants pour continuer.</CardDescription>
      </CardHeader>
      <CardContent className="space-y-4">
        <div className="space-y-2">
          <Label htmlFor="email">Email</Label>
          <Input id="email" type="email" placeholder="ton@email.com" />
        </div>
        <div className="space-y-2">
          <Label htmlFor="password">Mot de passe</Label>
          <Input id="password" type="password" />
        </div>
        <Button className="w-full">Se connecter</Button>
        <Button variant="outline" className="w-full">
          Continuer avec Google
        </Button>
      </CardContent>
    </Card>
  )
}

Remarques-tu le className sur Button et Card ? Tu peux toujours ajouter tes propres classes Tailwind - elles se fusionnent grâce à cn().

TSXtsx
// Personnaliser un composant shadcn avec tes propres classes
<Button
  variant="default"
  className="bg-gradient-to-r from-blue-600 to-cyan-500 rounded-xl"
>
  Mon bouton custom
</Button>

// Ça marche parce que cn() fusionne tes classes
// avec celles de la variante "default"

10.5Les composants les plus utiles

shadcn/ui propose plus de 40 composants. Voici ceux que tu utiliseras le plus souvent :

ComposantUsageBasé sur
ButtonBoutons avec variantesNatif
CardConteneurs structurésNatif
DialogModales accessiblesRadix UI
DropdownMenuMenus déroulantsRadix UI
TabsNavigation par ongletsRadix UI
Input / LabelChamps de formulaireNatif
SelectListes déroulantes styléesRadix UI
ToastNotifications temporairesRadix UI
SheetPanneau latéral (menu mobile)Radix UI
AccordionSections pliables (FAQ)Radix UI

10.6Personnaliser les composants

Comme le code est dans ton projet, tu le modifies directement :

TSXtsx
// components/ui/button.tsx - AVANT
const buttonVariants = cva(
  "inline-flex items-center justify-center gap-2 rounded-md ...",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground hover:bg-primary/90",
        // ...
      },
    },
  }
)

// components/ui/button.tsx - APRÈS (ta version)
const buttonVariants = cva(
  "inline-flex items-center justify-center gap-2 rounded-xl font-semibold ...",
  //                                                    ^^^^^^^^^^^^^^^^^^^
  //                                              Coins plus ronds + bold
  {
    variants: {
      variant: {
        default: "bg-blue-600 text-white hover:bg-blue-700 shadow-lg shadow-blue-500/25",
        //       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        //       Tes couleurs de marque + ombre
        brand: "bg-gradient-to-r from-blue-600 to-cyan-500 text-white hover:shadow-lg",
        //     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        //     Nouvelle variante que tu ajoutes !
      },
      size: {
        // ...
        xl: "h-14 rounded-2xl px-10 text-lg",
        //   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        //   Nouvelle taille
      },
    },
  }
)

10.7Le système de thème

shadcn/ui utilise des CSS variables pour ses couleurs. Ça se configure dans ton globals.css :

CSScss
/* app/globals.css */
@layer base {
  :root {
    --background: 0 0% 100%;        /* Blanc */
    --foreground: 222.2 84% 4.9%;   /* Texte sombre */
    --primary: 221.2 83.2% 53.3%;   /* Bleu principal */
    --primary-foreground: 210 40% 98%;
    --secondary: 210 40% 96.1%;
    --accent: 210 40% 96.1%;
    --destructive: 0 84.2% 60.2%;   /* Rouge erreur */
    --border: 214.3 31.8% 91.4%;
    --ring: 221.2 83.2% 53.3%;
    --radius: 0.5rem;               /* Rayon des coins */
  }

  .dark {
    --background: 222.2 84% 4.9%;   /* Fond sombre */
    --foreground: 210 40% 98%;      /* Texte clair */
    --primary: 217.2 91.2% 59.8%;
    /* ... mêmes variables, couleurs dark */
  }
}
CSScss
/* Changer le thème = changer les variables */
:root {
  /* Thème "BaliseTonSite" */
  --primary: 221 83% 53%;        /* blue-600 */
  --primary-foreground: 0 0% 100%;
  --accent: 189 94% 43%;         /* cyan-500 */
  --radius: 0.75rem;             /* Coins plus ronds */
}

/* Tous les composants shadcn changent automatiquement ! */

10.8Exemple complet : page de pricing

Construisons une vraie page avec plusieurs composants shadcn :

TSXtsx
import { Button } from "@/components/ui/button"
import {
  Card, CardContent, CardDescription,
  CardFooter, CardHeader, CardTitle
} from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"

const plans = [
  {
    name: "Gratuit",
    price: "0€",
    desc: "Les fondamentaux du web",
    features: ["HTML complet", "CSS complet", "JavaScript Vanilla"],
    cta: "Commencer",
    popular: false
  },
  {
    name: "Full-Stack Moderne",
    price: "19€/mois",
    desc: "Les technos pro",
    features: ["Tout le gratuit", "React", "Next.js", "TypeScript", "Tailwind"],
    cta: "Essayer gratuit",
    popular: true
  }
]

export function PricingPage() {
  return (
    <div className="grid md:grid-cols-2 gap-6 max-w-3xl mx-auto">
      {plans.map((plan) => (
        <Card
          key={plan.name}
          className={cn(
            "relative",
            plan.popular && "border-primary shadow-lg"
          )}
        >
          {plan.popular && (
            <Badge className="absolute -top-3 left-1/2 -translate-x-1/2">
              Populaire
            </Badge>
          )}
          <CardHeader>
            <CardTitle>{plan.name}</CardTitle>
            <CardDescription>{plan.desc}</CardDescription>
            <p className="text-3xl font-bold mt-2">{plan.price}</p>
          </CardHeader>
          <CardContent>
            <ul className="space-y-2">
              {plan.features.map((f) => (
                <li key={f} className="flex items-center gap-2 text-sm">
                  <i className="fa-solid fa-check text-green-500" />
                  {f}
                </li>
              ))}
            </ul>
          </CardContent>
          <CardFooter>
            <Button
              className="w-full"
              variant={plan.popular ? "default" : "outline"}
            >
              {plan.cta}
            </Button>
          </CardFooter>
        </Card>
      ))}
    </div>
  )
}

10.9Quand utiliser (ou pas) shadcn/ui

Utilise shadcn quand...

  • • Tu as besoin de composants accessibles (modale, menu, select)
  • • Tu veux un design cohérent rapidement
  • • Tu es sur un projet React / Next.js
  • • Tu veux personnaliser le rendu sans te battre avec une librairie
  • • Tu veux comprendre le code (tu peux le lire !)

Skip shadcn quand...

  • • Tu n'utilises pas React (vanilla JS, Vue, etc.)
  • • Tu veux un composant très custom qui n'existe pas
  • • Tu apprends encore les bases de Tailwind
  • • Ton projet est très petit (un composant suffit)
  • • Tu veux 0 dépendance externe

Verifie tes acquis

3 questions pour valider ce chapitre

1. Qu'est-ce qui differencie shadcn/ui des librairies de composants classiques (MUI, Chakra) ?

Valide et sauvegarde ce chapitre

Ne perds pas le fil de ton apprentissage. Chaque QCM terminé sauvegarde ton score. Crée ton profil gratuitement pour débloquer toutes les évaluations du site et retrouver tes résultats plus tard.

Commencer l'aventure
Déjà membre ?Connecte-toi