Pubblicato il

Cos’è shadcn/ui? Capirne scopo, vantaggi e utilizzo nelle app React moderne

Autori

    Cos’è shadcn/ui? Capirne scopo, vantaggi e utilizzo nelle app React moderne

    I progetti React moderni affrontano spesso un equilibrio difficile: usare una libreria di interfacce già pronta (rapida ma poco flessibile) oppure progettare un sistema proprio (flessibile ma lento da costruire)?
    È qui che entra in gioco shadcn/ui, un nuovo approccio allo sviluppo delle interfacce che offre controllo completo sul codice insieme alla cura di una libreria di componenti matura.

    In questa guida esploreremo cos’è shadcn/ui, perché è diventato così popolare tra gli sviluppatori React e Next.js e come usarlo per accelerare il lavoro sull’interfaccia senza perdere flessibilità.

    Cos’è shadcn/ui?

    shadcn/ui (spesso scritto semplicemente shadcn) non è un pacchetto npm tradizionale o una libreria di componenti con temi come Material UI o Chakra UI.
    È invece una raccolta di componenti React open source di alta qualità costruiti con:

    • Tailwind CSS (per gli stili)
    • Radix UI (per primitive accessibili e prive di stili)
    • Lucide Icons (per icone SVG pulite e coerenti)

    Ma ecco la particolarità:

    Invece di installarlo come dipendenza, copi il codice sorgente effettivo di ogni componente nel tuo progetto.

    Questo significa che ogni pulsante, finestra di dialogo o menu a discesa vive nella tua base di codice, modificabile, personalizzabile tramite temi e sotto il tuo controllo di versione.

    Puoi considerarlo un generatore di componenti o un kit iniziale per interfacce, non una dipendenza.

    Come funziona

    Il progetto shadcn/ui fornisce uno strumento CLI che genera i componenti direttamente nel tuo progetto:

    npx shadcn-ui@latest add button
    

    Questo comando scarica un componente Button accessibile e già pronto e lo aggiunge nella cartella locale components/ui/. Da lì puoi personalizzarlo, cambiare le classi Tailwind, modificare le varianti o adattarlo al tuo sistema di design.

    Ogni componente è semplicemente React + Tailwind + Radix, senza logica nascosta o dipendenza a runtime.

    Perché gli sviluppatori usano shadcn/ui

    1. Piena proprietà del codice

    A differenza delle librerie di interfacce in cui fai affidamento su dipendenze opache, shadcn ti offre il sorgente completo dei componenti. Puoi:

    • Modificare direttamente gli stili
    • Aggiungere proprietà o varianti
    • Integrare logica personalizzata (come analisi o autorizzazioni)

    È la tua interfaccia, con le tue regole, senza aspettare che i manutentori integrino una PR.

    2. Costruito su fondamenta collaudate

    Ogni componente è costruito usando:

    • Tailwind CSS → Stili basati su classi di utilità
    • Radix UI → Primitive di base accessibili e prive di stili
    • Lucide Icons → Icone SVG moderne
    • clsx / tailwind-variants → Gestione pulita delle classi condizionali

    Questo garantisce componenti accessibili e pronti per la produzione, che seguono fin da subito le buone pratiche dei sistemi di design.

    3. Coerenza e velocità

    Ottieni un punto di partenza bello e coerente per l’interfaccia:

    • Spaziatura, tipografia e sistema di colori coerenti
    • Modalità scura e temi tramite variabili CSS
    • Layout e pattern interattivi pronti all’uso (menu a discesa, finestre modali, suggerimenti)

    Puoi iniziare a rilasciare interfacce che sembrano progettate con cura, senza reinventare ogni componente.

    4. Nessun vincolo verso il fornitore

    Poiché i componenti sono semplice codice, puoi:

    • Crearne un fork
    • Sostituirli
    • Combinarli con altre librerie

    Se shadcn/ui dovesse cambiare o non bastasse più alle tue esigenze, i componenti rimarrebbero interamente tuoi.

    Componenti comuni ed esempi

    Ecco alcuni dei componenti più popolari di shadcn/ui e come appaiono in azione.

    Esempio 1: pulsante

    import { Button } from '@/components/ui/button'
    
    export default function Example() {
      return (
        <div className="space-x-2">
          <Button>Default</Button>
          <Button variant="outline">Outline</Button>
          <Button variant="destructive">Delete</Button>
        </div>
      )
    }
    

    Perché è ottimo:

    • Varianti integrate come outline, destructive e ghost.
    • Completamente personalizzabile tramite temi con i token di design di Tailwind.
    • Abbastanza semplice da personalizzare senza perdere coerenza.

    Comprendere le varianti del pulsante

    Quando esegui:

    npx shadcn-ui@latest add button
    

    La CLI crea un file button.tsx dentro components/ui/, di solito così:

    // components/ui/button.tsx
    import * as React from 'react'
    import { cva } 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 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none',
      {
        variants: {
          variant: {
            default: 'bg-primary text-primary-foreground hover:bg-primary/90',
            outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
            destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
            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> {}
    
    function Button({ className, variant, size, ...props }: ButtonProps) {
      return <button className={cn(buttonVariants({ variant, size, className }))} {...props} />
    }
    
    export { Button, buttonVariants }
    

    Quindi cosa sono outline, destructive e ghost?

    Sono varianti di stile predefinite in quella configurazione buttonVariants. Non devi definirle manualmente, sono incluse per impostazione predefinita nel componente generato.

    Ogni variante corrisponde a un diverso schema di classi Tailwind:

    VarianteStile visivoUso tipico
    defaultPulsante principale pieno (bg-primary, testo bianco)Invito all’azione principale (ad esempio “Salva”, “Invia”).
    outlineSfondo trasparente, bordo visibileAzione secondaria o con minore enfasi (“Annulla”).
    destructiveColore rosso/di pericolo (bg-destructive)Azioni con conseguenze distruttive (“Elimina”, “Rimuovi”).
    ghostStile minimale, effetto delicato al passaggio del mousePulsanti con poca enfasi (ad esempio icone, link secondari).
    linkStile di un link testualePer la navigazione in linea all’interno del testo.

    Quindi, quando scrivi:

    <Button variant="destructive">Delete</Button>
    

    stai dicendo al componente Button di usare lo stile rosso di pericolo, perché la variante corrisponde a bg-destructive e text-destructive-foreground.

    Cosa sono i “token di design di Tailwind”?

    I “token di design” sono variabili con un nome per colori e spaziatura, usate in tutto il tema. In shadcn/ui, sono definiti in globals.css (o tailwind.config.js) in questo modo:

    :root {
      --background: 0 0% 100%;
      --foreground: 222.2 47.4% 11.2%;
      --primary: 222.2 47.4% 11.2%;
      --primary-foreground: 210 40% 98%;
      --destructive: 0 84.2% 60.2%;
      --destructive-foreground: 210 40% 98%;
      /* etc. */
    }
    

    Tailwind li richiama poi come “token” usando la sintassi theme() o scorciatoie per le classi:

    className = 'bg-primary text-primary-foreground hover:bg-primary/90'
    

    Questi token:

    • Mantengono i colori coerenti tra i componenti.
    • Supportano automaticamente la modalità scura tramite variabili CSS.
    • Permettono a designer e sviluppatori di condividere un unico linguaggio visivo (ad esempio “primario”, “accento”, “distruttivo”) invece di codici esadecimali grezzi.

    Perché è utile

    • Puoi aggiungere o modificare facilmente varianti (ad esempio success, warning) nello stesso button.tsx.
    • L’interfaccia rimane visivamente coerente poiché tutte le varianti attingono a token di design condivisi.
    • Aggiornare il tema (colori, spaziatura ecc.) aggiorna istantaneamente tutti i componenti.
    • È flessibile, usa le varianti integrate oppure estendile per adattarle al tuo marchio.

    In breve: Quelle varianti (outline, destructive, ghost ecc.) sono stili predefiniti basati su token di design che puoi estendere o sovrascrivere. Contribuiscono a rendere i componenti shadcn/ui pronti per la produzione fin da subito, coerenti, capaci di adattarsi al tema e facilmente personalizzabili.

    Esempio 2: finestra di dialogo (modale)

    import {
      Dialog,
      DialogContent,
      DialogHeader,
      DialogTitle,
      DialogTrigger,
    } from '@/components/ui/dialog'
    
    export default function Example() {
      return (
        <Dialog>
          <DialogTrigger asChild>
            <Button>Open Dialog</Button>
          </DialogTrigger>
          <DialogContent>
            <DialogHeader>
              <DialogTitle>Confirm Action</DialogTitle>
            </DialogHeader>
            <p>This action cannot be undone.</p>
          </DialogContent>
        </Dialog>
      )
    }
    

    Perché è ottima: La finestra di dialogo usa internamente primitive Radix, quindi è accessibile da tastiera, compatibile con gli screen reader e responsive fin da subito.

    Esempio 3: componenti di input e moduli

    import { Input } from '@/components/ui/input'
    import { Label } from '@/components/ui/label'
    
    export default function LoginForm() {
      return (
        <form className="space-y-4">
          <div>
            <Label htmlFor="email">Email</Label>
            <Input id="email" type="email" placeholder="you@example.com" />
          </div>
          <Button type="submit">Login</Button>
        </form>
      )
    }
    

    Puliti, minimali e facili da estendere per qualsiasi pagina di autenticazione o impostazioni.

    Quando usare e quando non usare shadcn/ui

    ✅ Usalo quando

    • Stai creando un prodotto personalizzato (SaaS, dashboard, app) e vuoi rapidamente un’interfaccia coerente.
    • Hai bisogno di pieno controllo su design e accessibilità.
    • Usi già Tailwind CSS e Next.js.

    ❌ Evitalo quando

    • Preferisci un sistema di design pronto all’uso (come MUI, Chakra UI).
    • Non usi Tailwind (il legame è stretto).
    • Vuoi procedere estremamente in fretta senza toccare il codice dei componenti.

    Per iniziare (configurazione rapida)

    1. Installa Tailwind CSS (se non è già presente):

      npx create-next-app my-app
      cd my-app
      npx tailwindcss init -p
      
    2. Inizializza shadcn/ui

      npx shadcn-ui@latest init
      
    3. Aggiungi componenti

      npx shadcn-ui@latest add button card dialog input
      
    4. Importa e personalizza I nuovi componenti appariranno in components/ui/.

    🧩 shadcn e altre librerie di interfacce a confronto

    Funzionalitàshadcn/uiChakra UIMaterial UIHeadless UI
    Proprietà del codice✅ È tuo❌ Controllato dalla libreria❌ Controllato dalla libreria✅ È tuo
    StiliTailwindStyled SystemEmotion / CSSTailwind
    Accessibilità✅ Radix UI✅✅✅
    Temi✅ (variabili CSS + Tailwind)✅✅⚙️ Personalizzati
    Curva di apprendimento🟢 Bassa🟢 Media🔴 Alta🟡 Media
    Libertà di personalizzazione🟢 Completa🟡 Parziale🔴 Limitata🟢 Completa

    Punti chiave

    • shadcn/ui non è un pacchetto, è un modello di componenti che copi e possiedi.
    • Ottieni controllo reale, nessun vincolo e un’ottima esperienza di sviluppo.
    • Costruito con Tailwind, Radix e pattern React moderni, è ideale per i team che vogliono fondamenta di design pulite, accessibili ed estendibili.

    È il perfetto punto d’incontro tra “costruire tutto da soli” e “dipendere da una pesante libreria di interfacce”.