Back

Comment intégrer ShadCN avec Next.js

Comment intégrer ShadCN avec Next.js

Configurer une bibliothèque de composants d’interface moderne ne devrait pas ressembler à assembler des meubles sans notice. Si vous développez avec Next.js 15 et souhaitez des composants beaux et personnalisables sans la frustration des boîtes noires des bibliothèques traditionnelles, ce guide vous montre exactement comment intégrer Shadcn UI dans votre projet.

Cet article couvre l’ensemble du processus de configuration de Shadcn UI avec Next.js, depuis la création initiale du projet jusqu’à l’installation des composants, la configuration de Tailwind CSS et l’implémentation appropriée du mode sombre. Vous apprendrez à éviter les problèmes de compatibilité courants avec React 19 et à mettre en place un système de composants prêt pour la production que vous possédez réellement.

Points clés à retenir

  • Shadcn UI copie le code source des composants directement dans votre projet, vous donnant un contrôle complet sur la propriété et la personnalisation
  • La CLI gère l’installation et la gestion des dépendances, rendant la configuration simple même avec les considérations de compatibilité React 19
  • L’intégration Tailwind CSS est transparente, avec un support de thématisation intégré et une implémentation du mode sombre
  • Les composants sont agnostiques au framework et fonctionnent avec App Router et Pages Router dans Next.js

Pourquoi choisir Shadcn UI pour votre projet Next.js

Shadcn UI adopte une approche fondamentalement différente des bibliothèques de composants. Au lieu d’installer une dépendance que vous ne pouvez pas modifier, Shadcn copie le code source des composants directement dans votre projet. Cela signifie que vous obtenez :

  • Une propriété complète de vos composants - modifiez n’importe quoi, n’importe quand
  • Un design Tailwind-first qui s’intègre parfaitement avec vos styles existants
  • Zéro surcharge d’exécution - les composants ne sont que du code dans votre projet
  • Sécurité de type intégrée dès le départ
  • Accessibilité gérée correctement par défaut grâce aux primitives Radix UI

Cette approche brille particulièrement avec l’App Router de Next.js 15, où les composants serveur et les limites client nécessitent une conception de composants soigneuse.

Prérequis et configuration initiale

Avant de commencer votre configuration Shadcn UI avec Next.js, assurez-vous d’avoir :

  • Node.js 18.17 ou supérieur
  • Un gestionnaire de paquets (npm, pnpm, yarn ou bun)
  • Une familiarité de base avec React et Tailwind CSS

Création de votre projet Next.js 15

Commencez par créer un nouveau projet Next.js avec TypeScript et Tailwind CSS :

npx create-next-app@latest my-app --typescript --tailwind --app

Lorsque vous y êtes invité, sélectionnez ces options :

  • ✓ Souhaitez-vous utiliser ESLint ? Oui
  • ✓ Souhaitez-vous utiliser le répertoire src/ ? Oui (recommandé)
  • ✓ Souhaitez-vous personnaliser l’alias d’importation par défaut ? Non

Naviguez vers votre projet :

cd my-app

Installation et configuration de Shadcn UI

La CLI Shadcn gère la plupart du travail lourd pour l’installation des composants. Exécutez la commande d’initialisation :

npx shadcn@latest init

Vous verrez plusieurs invites de configuration :

  1. Style : Choisissez votre style préféré (Default/New York)
  2. Couleur de base : Sélectionnez parmi les schémas de couleurs disponibles
  3. Variables CSS : Utilisez Oui pour un meilleur support de thématisation

Gestion des dépendances pairs React 19

Si vous utilisez npm avec React 19, vous rencontrerez des avertissements de dépendances pairs :

npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree

La CLI vous invitera à choisir une stratégie de résolution :

? How would you like to proceed?
 Use --force
  Use --legacy-peer-deps

Sélectionnez --legacy-peer-deps pour l’approche la plus sûre. Les utilisateurs de pnpm, yarn ou bun ne verront pas ces avertissements et peuvent procéder normalement.

Ajout de vos premiers composants

Avec Shadcn initialisé, ajoutez des composants en utilisant la CLI :

npx shadcn@latest add button card

Cette commande :

  1. Télécharge le code source du composant
  2. Le place dans src/components/ui/
  3. Installe toutes les dépendances requises (comme les primitives Radix UI)
  4. Met à jour vos importations de composants

Voici à quoi ressemble un composant bouton Shadcn typique après installation :

// src/components/ui/button.tsx
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"

const buttonVariants = cva(
  "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2",
  {
    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",
        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> {
  asChild?: boolean
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, asChild = false, ...props }, ref) => {
    const Comp = asChild ? Slot : "button"
    return (
      <Comp
        className={cn(buttonVariants({ variant, size, className }))}
        ref={ref}
        {...props}
      />
    )
  }
)
Button.displayName = "Button"

export { Button, buttonVariants }

Remarquez comme le composant est entièrement personnalisable - vous pouvez modifier les variantes, en ajouter de nouvelles, ou changer complètement le style.

Configuration de Tailwind CSS pour Shadcn

Shadcn UI fonctionne avec Tailwind CSS v3 et v4. La CLI configure automatiquement votre tailwind.config.js lors de l’initialisation. Voici ce qui est ajouté :

// tailwind.config.js
module.exports = {
  darkMode: ["class"],
  content: [
    './pages/**/*.{ts,tsx}',
    './components/**/*.{ts,tsx}',
    './app/**/*.{ts,tsx}',
    './src/**/*.{ts,tsx}',
  ],
  theme: {
    container: {
      center: true,
      padding: "2rem",
      screens: {
        "2xl": "1400px",
      },
    },
    extend: {
      colors: {
        border: "hsl(var(--border))",
        input: "hsl(var(--input))",
        ring: "hsl(var(--ring))",
        background: "hsl(var(--background))",
        foreground: "hsl(var(--foreground))",
        primary: {
          DEFAULT: "hsl(var(--primary))",
          foreground: "hsl(var(--primary-foreground))",
        },
        secondary: {
          DEFAULT: "hsl(var(--secondary))",
          foreground: "hsl(var(--secondary-foreground))",
        },
        destructive: {
          DEFAULT: "hsl(var(--destructive))",
          foreground: "hsl(var(--destructive-foreground))",
        },
        muted: {
          DEFAULT: "hsl(var(--muted))",
          foreground: "hsl(var(--muted-foreground))",
        },
        accent: {
          DEFAULT: "hsl(var(--accent))",
          foreground: "hsl(var(--accent-foreground))",
        },
        popover: {
          DEFAULT: "hsl(var(--popover))",
          foreground: "hsl(var(--popover-foreground))",
        },
        card: {
          DEFAULT: "hsl(var(--card))",
          foreground: "hsl(var(--card-foreground))",
        },
      },
      borderRadius: {
        lg: "var(--radius)",
        md: "calc(var(--radius) - 2px)",
        sm: "calc(var(--radius) - 4px)",
      },
    },
  },
  plugins: [require("tailwindcss-animate")],
}

Mise à jour de votre globals.css

La CLI met également à jour votre globals.css avec les variables CSS pour la thématisation :

@tailwind base;
@tailwind components;
@tailwind utilities;

@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: 221.2 83.2% 53.3%;
    --primary-foreground: 210 40% 98%;
    --secondary: 210 40% 96%;
    --secondary-foreground: 222.2 84% 4.9%;
    --muted: 210 40% 96%;
    --muted-foreground: 215.4 16.3% 46.9%;
    --accent: 210 40% 96%;
    --accent-foreground: 222.2 84% 4.9%;
    --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: 221.2 83.2% 53.3%;
    --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: 217.2 91.2% 59.8%;
    --primary-foreground: 222.2 84% 4.9%;
    --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: 224.3 76.3% 94.1%;
  }
}

@layer base {
  * {
    @apply border-border;
  }
  body {
    @apply bg-background text-foreground;
  }
}

Implémentation du mode sombre avec next-themes

Pour une implémentation du mode sombre prête pour la production, utilisez next-themes pour éviter les problèmes d’hydratation.

Installation de next-themes

npm install next-themes

Création d’un fournisseur de thème

Créez src/components/theme-provider.tsx :

"use client"

import * as React from "react"
import { ThemeProvider as NextThemesProvider } from "next-themes"
import { type ThemeProviderProps } from "next-themes/dist/types"

export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
  return <NextThemesProvider {...props}>{children}</NextThemesProvider>
}

Mise à jour de votre layout racine

Modifiez src/app/layout.tsx :

import { ThemeProvider } from "@/components/theme-provider"

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
          disableTransitionOnChange
        >
          {children}
        </ThemeProvider>
      </body>
    </html>
  )
}

Création d’un composant de basculement de thème

"use client"

import { Moon, Sun } from "lucide-react"
import { useTheme } from "next-themes"
import { Button } from "@/components/ui/button"

export function ThemeToggle() {
  const { setTheme, theme } = useTheme()

  return (
    <Button
      variant="ghost"
      size="icon"
      onClick={() => setTheme(theme === "light" ? "dark" : "light")}
    >
      <Sun className="h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
      <Moon className="absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
      <span className="sr-only">Basculer le thème</span>
    </Button>
  )
}

Travailler avec les changements de React 19

React 19 introduit plusieurs changements qui affectent la façon dont vous travaillez avec les composants Shadcn :

Gestion simplifiée des ref

React 19 permet de passer ref comme une prop normale, éliminant le besoin de forwardRef dans de nombreux cas :

// Ancienne approche (React 18)
const Input = React.forwardRef<HTMLInputElement, InputProps>(
  ({ className, ...props }, ref) => {
    return <input ref={ref} className={className} {...props} />
  }
)

// Nouvelle approche (React 19)
function Input({ className, ref, ...props }: InputProps & { ref?: React.Ref<HTMLInputElement> }) {
  return <input ref={ref} className={className} {...props} />
}

Nouveaux hooks de formulaire

Les hooks useActionState et useFormStatus de React 19 fonctionnent parfaitement avec les composants de formulaire Shadcn :

import { useActionState } from "react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"

function ContactForm() {
  const [state, formAction] = useActionState(async (prevState, formData) => {
    // Logique d'action serveur ici
    const email = formData.get("email")
    // Traiter le formulaire...
    return { success: true }
  }, { success: false })

  return (
    <form action={formAction}>
      <Input name="email" type="email" required />
      <Button type="submit">Envoyer</Button>
      {state.success && <p>Merci pour votre abonnement !</p>}
    </form>
  )
}

Problèmes courants et solutions

Erreurs de build avec Tailwind CSS

Si vous rencontrez des erreurs de build après la configuration, assurez-vous que votre configuration PostCSS est correcte :

// postcss.config.js
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

Style de composant non appliqué

Vérifiez que votre globals.css est importé dans votre layout racine :

import "./globals.css"

Erreurs TypeScript avec les props de composant

Assurez-vous que votre tsconfig.json inclut les chemins corrects :

{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

Meilleures pratiques pour la production

  1. Personnalisez l’utilitaire cn : La fonction cn dans lib/utils.ts fusionne les classes intelligemment. Étendez-la pour vos besoins spécifiques.

  2. Créez des variantes de composants : Utilisez CVA (class-variance-authority) pour créer des variantes de composants cohérentes dans votre application.

  3. Optimisez la taille du bundle : N’installez que les composants que vous utilisez réellement. Chaque composant est indépendant.

  4. Testez l’accessibilité : Les composants Shadcn utilisent les primitives Radix UI qui gèrent l’accessibilité, mais testez toujours avec les lecteurs d’écran.

  5. Contrôle de version : Puisque les composants sont copiés dans votre projet, commitez-les dans le contrôle de version et suivez les changements.

Conclusion

Configurer Shadcn UI avec Next.js vous donne un système de composants moderne qui équilibre flexibilité et vitesse de développement. Contrairement aux bibliothèques de composants traditionnelles, vous possédez chaque ligne de code, pouvez tout personnaliser et n’êtes pas enfermé dans les décisions de conception de quelqu’un d’autre.

La combinaison de l’App Router de Next.js 15, des améliorations de React 19 et de Tailwind CSS crée une base puissante pour construire des applications web modernes. Avec l’approche de Shadcn de copier les composants plutôt que de les installer comme dépendances, vous obtenez le meilleur des deux mondes : développement rapide et contrôle complet.

FAQ

Lors de l'utilisation de npm avec React 19, vous devrez utiliser le flag --legacy-peer-deps pendant l'installation des composants en raison de conflits de dépendances pairs. Les gestionnaires de paquets comme pnpm, yarn et bun gèrent ces dépendances plus gracieusement et ne nécessitent aucun flag spécial. Le résultat final est le même quel que soit le gestionnaire de paquets que vous choisissez.

Oui, Shadcn UI fonctionne avec App Router et Pages Router. Le code des composants lui-même est agnostique au framework. La principale différence réside dans la façon dont vous implémentez les fournisseurs comme le fournisseur de thème - dans Pages Router, vous enveloppez votre app dans _app.tsx au lieu du layout racine.

Puisque Shadcn copie les composants directement dans votre projet, vous pouvez les modifier comme n'importe quel autre code. Ouvrez le fichier du composant dans src/components/ui/, ajustez les classes Tailwind ou les variantes CVA, et sauvegardez. Vous pouvez également modifier les variables de thème globales dans votre CSS pour changer les couleurs et l'espacement sur tous les composants à la fois.

Non, vous ne devriez installer que les composants dont vous avez besoin. Chaque composant est indépendant et inclut ses propres dépendances. Cela maintient la taille de votre bundle minimale et votre projet organisé. Utilisez npx shadcn@latest add [nom-du-composant] pour ajouter des composants individuels quand nécessaire.

Puisque les composants sont copiés dans votre projet, ils ne se mettront pas automatiquement à jour quand Shadcn publie de nouvelles versions. Vous pouvez manuellement vérifier la documentation Shadcn UI pour les mises à jour et soit relancer la commande add pour des composants spécifiques ou appliquer manuellement les changements. Cela vous donne le contrôle sur quand et comment les composants sont mis à jour dans votre projet.

Listen to your bugs 🧘, with OpenReplay

See how users use your app and resolve issues fast.
Loved by thousands of developers