shadcn/ui, Radix, Headless UI, daisyUI, Flowbite et MUI diffèrent surtout par le style : Radix et Headless UI sont sans style, daisyUI et Flowbite sont du Tailwind déjà stylé, MUI suit Material Design, et shadcn/ui copie du code stylé dans votre projet. Voici les mêmes six composants dans chacune, plus laquelle choisir.
Ces six bibliothèques résolvent des problèmes différents, c'est pourquoi la question « laquelle est la meilleure » n'a pas de réponse unique. Deux d'entre elles (Radix Primitives et Headless UI) fournissent le comportement et l'accessibilité sans aucun style. Deux autres (daisyUI et Flowbite) fournissent un rendu Tailwind déjà fini. MUI fournit un design system complet avec son propre moteur de styles. shadcn/ui n'est pas du tout un paquet : il copie le code source de composants stylés dans votre projet, construits sur une couche headless sous-jacente.
Tous les faits ci-dessous (versions, licences, prérequis) ont été vérifiés en octobre 2026 dans la documentation officielle, le dépôt GitHub et la fiche npm de chaque projet. Les versions évoluent vite, considérez donc les chiffres comme valables « au moment de la rédaction ».
| shadcn/ui | Radix Primitives | Headless UI | daisyUI | Flowbite React | MUI (Material UI) | |
|---|---|---|---|---|---|---|
| Ce que vous installez | une CLI qui copie le code source des composants dans votre dépôt | des paquets npm (radix-ui ou un par composant) | @headlessui/react | le plugin Tailwind daisyui | flowbite-react | @mui/material plus Emotion |
| Modèle de style | classes Tailwind dans du code qui vous appartient ; thème via des variables CSS | sans style ; vous ajoutez les classes, l'état passe par des attributs data-state | sans style ; l'état passe par des attributs data-*, conçu pour Tailwind | classes CSS de composants (btn, modal) et thèmes | composants Tailwind déjà stylés, personnalisables par thème | Material Design, CSS-in-JS (prop sx, objet de thème) |
| Version (oct. 2026) | CLI shadcn 4.x (aucun paquet d'exécution) | radix-ui 1.8 ; paquets individuels en 1.x et 2.x | 2.2 | 5.x | 0.12 (avant la 1.0) | 9.x |
| Licence | MIT | MIT | MIT | MIT | MIT | MIT (les offres payantes MUI X Pro/Premium sont à part) |
| React requis | oui | 16.8 à 19 | 18 ou 19 | non (HTML et CSS simples) | 18 ou 19 | 17, 18 ou 19 |
| Tailwind requis | oui (v4 dans la documentation actuelle) | non | non, mais pensé pour lui | oui, Tailwind 4 pour daisyUI 5 | oui, Tailwind 3 ou 4 | non |
| Approche de l'accessibilité | héritée de sa couche de primitives (Base UI ou Radix) | suit les WAI-ARIA Authoring Practices ; clavier et focus intégrés | présenté comme « entièrement accessible » ; clavier et focus intégrés | s'appuie sur les éléments natifs (<dialog>, <details>) ; vous ajoutez ARIA et le comportement | composants React avec positionnement Floating UI ; testez vous-même l'usage au clavier | gestion ARIA et clavier par composant, documentée composant par composant |
| Propose un toast | oui, via Sonner | oui (Toast) | non | oui (classe de mise en page seulement) | oui (Toast) | oui (Snackbar) |
Nous avons volontairement laissé le poids du bundle hors du tableau. Les chiffres dépendent fortement des composants que vous importez, du tree-shaking effectué par votre bundler et (pour daisyUI) de la quantité de CSS que Tailwind génère pour les classes utilisées. Un chiffre unique en « kB » par bibliothèque induirait plus en erreur qu'il n'aiderait. Mesurez votre propre build : lancez l'analyseur de votre bundler avant et après l'ajout des composants dont vous avez réellement besoin.
Ce que c'est. shadcn/ui se décrit comme « pas une bibliothèque de composants » mais « la façon de construire votre bibliothèque de composants ». Vous lancez sa CLI (npx shadcn init, puis npx shadcn add button) et elle écrit le code source du composant dans votre projet. À partir de là, le code vous appartient et vous le modifiez librement ; il n'y a aucun paquet shadcn d'exécution à mettre à jour.
Style. Des classes Tailwind CSS dans les fichiers copiés, avec les couleurs et les arrondis en variables CSS pour changer de thème à un seul endroit. Les variantes sont généralement définies avec class-variance-authority.
Sous le capot. Les parties interactives reposent sur une bibliothèque headless. D'après le changelog de shadcn/ui, les nouveaux projets utilisent Base UI par défaut depuis juillet 2026, et Radix « n'est pas abandonné » : les deux sont pris en charge. Son toast est construit sur Sonner, un paquet de toasts open source distinct.
Licence et version. MIT (dépôt GitHub shadcn-ui/ui). Le paquet CLI shadcn était en 4.x sur npm au moment de la rédaction.
Points de vigilance. Comme le code vous appartient, sa maintenance aussi. Les correctifs en amont n'atteignent pas automatiquement vos copies, et les projets finissent par diverger avec le temps.
Ce que c'est. Un ensemble de composants React accessibles et sans style (dialog, menu déroulant, onglets, switch, toast, tooltip, popover et plus). Vous pouvez tout installer depuis le paquet unique radix-ui (1.8 au moment de la rédaction) ou un paquet par composant, comme @radix-ui/react-dialog.
Style. Aucun par défaut. Chaque partie produit un élément simple que vous pouvez styler avec n'importe quelle méthode. L'état est exposé par des attributs data, par exemple data-state="open" ou data-highlighted, qui se combinent bien avec les variantes data-[state=open]: de Tailwind.
Accessibilité. La documentation de Radix indique que les primitives suivent les WAI-ARIA Authoring Practices, prennent en charge le clavier et gèrent le focus (par exemple en le piégeant dans un dialogue ouvert et en le restituant à la fermeture).
Licence et prérequis. MIT. La plage de peer dependencies npm couvre React 16.8 à 19.
Points de vigilance. Vous écrivez vous-même tous les styles. Radix ne fournit pas de primitive Button : utilisez un <button> natif, ou l'utilitaire Slot pour fusionner les props dans votre propre élément.
Ce que c'est. Des composants sans style créés par l'équipe de Tailwind CSS. Le paquet React, @headlessui/react (2.2 au moment de la rédaction), comprend Button, Checkbox, Combobox, Dialog, Disclosure, Fieldset, Input, Listbox, Menu (menu déroulant), Popover, Radio Group, Select, Switch, Tabs, Textarea et Transition.
Style. Aucun. Les composants exposent leur état via des attributs data (data-open, data-checked, data-selected, data-focus), que vous stylez avec des variantes Tailwind comme data-checked:bg-blue-600 (syntaxe Tailwind 4) ou data-[checked]:bg-blue-600 (Tailwind 3).
Accessibilité. Son site décrit les composants comme « totalement sans style, entièrement accessibles ». La navigation au clavier, le piégeage du focus dans les dialogues et le câblage ARIA sont intégrés.
Licence et prérequis. MIT. React 18 ou 19 (peer dependency npm).
Points de vigilance. L'ensemble est plus restreint que Radix : il n'y a ni toast, ni tooltip, ni slider, il vous faudra donc un autre paquet pour ceux-là.
Ce que c'est. Un plugin Tailwind CSS qui ajoute des noms de classes de composants comme btn, modal, dropdown, tabs, toggle et toast, ainsi qu'un ensemble de thèmes de couleurs. Il fournit du CSS, pas des composants React, et fonctionne donc avec n'importe quel framework, ou sans.
Style. Des classes sémantiques avec modificateurs (btn btn-primary btn-sm) que vous pouvez toujours combiner avec les utilitaires Tailwind habituels. Les thèmes changent via des variables CSS.
Prérequis. daisyUI 5 (sorti pour la première fois en février 2025, en 5.x au moment de la rédaction) cible Tailwind CSS 4 ; son guide de mise à niveau vous fait d'abord mettre à jour Tailwind. Vous l'installez avec une seule ligne @plugin "daisyui"; dans votre CSS.
Accessibilité. Comme il ne s'agit que de CSS, le comportement vient du HTML natif : la modale utilise l'élément <dialog> (Escape la ferme et le navigateur gère le focus), les menus déroulants utilisent <details> ou l'API popover. Tout le reste (ARIA pour les onglets, annonce des toasts) est à votre charge.
Licence. MIT.
Points de vigilance. En React, vous câblez l'état vous-même (par exemple en appelant showModal() via une ref). Il n'y a aucune gestion des flèches du clavier pour les menus ou les onglets, sauf si vous l'ajoutez.
Ce que c'est. Des composants React (flowbite-react) stylés avec Tailwind, issus du projet Flowbite. Il comprend Button, Modal, Dropdown, Tabs, ToggleSwitch, Toast, Navbar, Sidebar, Datepicker et plus.
Style. Déjà stylés avec des classes Tailwind, personnalisables via un objet de thème et une prop theme sur chaque composant.
Prérequis et version. Version 0.12 au moment de la rédaction : elle n'a pas atteint la 1.0, attendez-vous donc à des changements cassants entre versions mineures. Les peer dependencies npm sont React 18 ou 19 et Tailwind CSS 3 ou 4. Elle utilise Floating UI pour positionner menus déroulants et tooltips.
Accessibilité. Les composants produisent les rôles attendus pour les modèles courants, mais nous n'avons pas trouvé de déclaration d'accessibilité publiée comparable à celle de Radix. Testez le comportement au clavier et au lecteur d'écran des composants sur lesquels vous comptez.
Licence. MIT (la bibliothèque open source ; Flowbite vend aussi séparément des blocs Pro et des fichiers de design).
Ce que c'est. Selon ses propres termes, « une bibliothèque de composants React open source qui implémente le Material Design de Google ». C'est l'ensemble le plus vaste de ce comparatif, avec des composants d'affichage de données, de saisie, de navigation, de mise en page et de retour utilisateur.
Style. CSS-in-JS. Par défaut, il utilise Emotion (@emotion/react et @emotion/styled sont des peer dependencies de @mui/material 9). Vous le personnalisez avec createTheme et stylez les cas ponctuels avec la prop sx. Il peut cohabiter avec Tailwind, mais les deux systèmes de thème ne partagent pas de tokens tant que vous ne les reliez pas.
Version et licence. Version 9 (la 9.0.0 a été publiée en avril 2026). La bibliothèque de base est sous MIT. Les fonctions avancées de grille de données, de dates et de graphiques de MUI X Pro et Premium relèvent de licences commerciales.
Accessibilité. Les composants sont livrés avec des attributs ARIA et une gestion du clavier, et les pages de composants de la documentation MUI décrivent le comportement d'accessibilité de chacun.
Points de vigilance. S'éloigner du style Material demande un vrai travail de thème, et le CSS-in-JS exige une configuration supplémentaire avec les React Server Components.
Ci-dessous, le même bouton, dialogue, menu déroulant, onglets, interrupteur et toast sont écrits six fois, une fois avec l'API documentée de chaque bibliothèque. Chaque extrait est du code original écrit pour cette page (et non copié de la documentation des bibliothèques), utilise la version majeure actuelle de chaque bibliothèque et se limite aux parties qui diffèrent. Des versions interactives et cliquables sont affichées pour Radix Primitives uniquement, car Radix est la bibliothèque installée sur ce site. Elles sont stylées avec nos propres classes Tailwind, puisque Radix ne fournit aucun style. Nous ne montrons pas de versions interactives des cinq autres bibliothèques, afin que rien ici n'imite leur apparence.
La plupart des bibliothèques fournissent un bouton avec des variantes. Remarquez que Radix et Headless UI vous laissent l'apparence, tandis que daisyUI exprime les variantes sous forme de classes et les autres sous forme de props.
// after: npx shadcn add button
import { Button } from "@/components/ui/button";
<Button>Save changes</Button>
<Button variant="outline" size="sm">Cancel</Button>
<Button variant="destructive" disabled>Delete</Button>
<Button asChild><a href="/pricing">See pricing</a></Button>
// Radix has no Button primitive. Use a native button,
// or Slot to pass props onto your own element.
import { Slot } from "radix-ui";
function Button({ asChild, className, ...props }) {
const Comp = asChild ? Slot.Root : "button";
return <Comp className={"rounded-lg bg-black px-4 py-2 text-white " + className} {...props} />;
}
import { Button } from "@headlessui/react";
<Button className="rounded-lg bg-black px-4 py-2 text-white
data-hover:bg-black/80 data-active:bg-black/70
data-disabled:opacity-50">
Save changes
</Button>
<button className="btn btn-primary">Save changes</button>
<button className="btn btn-outline btn-sm">Cancel</button>
<button className="btn btn-error" disabled>Delete</button>
<a className="btn btn-link" href="/pricing">See pricing</a>
import { Button } from "flowbite-react";
<Button>Save changes</Button>
<Button color="alternative" size="sm">Cancel</Button>
<Button color="red" disabled>Delete</Button>
<Button href="/pricing">See pricing</Button>
import Button from "@mui/material/Button";
<Button variant="contained">Save changes</Button>
<Button variant="outlined" size="small">Cancel</Button>
<Button variant="contained" color="error" disabled>Delete</Button>
<Button href="/pricing">See pricing</Button>
C'est dans le dialogue que se cache le travail d'accessibilité : piège à focus, fermeture avec Escape, retour du focus au déclencheur, étiquetage. Les bibliothèques headless et MUI le font en JavaScript ; daisyUI s'appuie sur l'élément natif <dialog>, qui gère Escape et le focus dans le navigateur.
import {
Dialog, DialogContent, DialogDescription, DialogFooter,
DialogHeader, DialogTitle, DialogTrigger, DialogClose,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
<Dialog>
<DialogTrigger asChild><Button>Edit profile</Button></DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Edit profile</DialogTitle>
<DialogDescription>Update your display name.</DialogDescription>
</DialogHeader>
{/* form fields */}
<DialogFooter>
<DialogClose asChild><Button variant="outline">Cancel</Button></DialogClose>
<Button type="submit">Save</Button>
</DialogFooter>
</DialogContent>
</Dialog>
import { Dialog } from "radix-ui";
<Dialog.Root>
<Dialog.Trigger className="btn-dark">Edit profile</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-black/40" />
<Dialog.Content className="fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-xl bg-white p-5">
<Dialog.Title>Edit profile</Dialog.Title>
<Dialog.Description>Update your display name.</Dialog.Description>
{/* form fields */}
<Dialog.Close>Cancel</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
import { Dialog, DialogPanel, DialogTitle, DialogBackdrop } from "@headlessui/react";
import { useState } from "react";
const [open, setOpen] = useState(false);
<button onClick={() => setOpen(true)}>Edit profile</button>
<Dialog open={open} onClose={setOpen} className="relative z-50">
<DialogBackdrop className="fixed inset-0 bg-black/40" />
<div className="fixed inset-0 flex items-center justify-center p-4">
<DialogPanel className="max-w-md rounded-xl bg-white p-5">
<DialogTitle className="font-semibold">Edit profile</DialogTitle>
{/* form fields */}
<button onClick={() => setOpen(false)}>Cancel</button>
</DialogPanel>
</div>
</Dialog>
import { useRef } from "react";
const ref = useRef<HTMLDialogElement>(null);
<button className="btn" onClick={() => ref.current?.showModal()}>Edit profile</button>
<dialog ref={ref} className="modal">
<div className="modal-box">
<h3 className="text-lg font-bold">Edit profile</h3>
{/* form fields */}
<div className="modal-action">
<form method="dialog"><button className="btn">Cancel</button></form>
</div>
</div>
<form method="dialog" className="modal-backdrop"><button>close</button></form>
</dialog>
import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from "flowbite-react";
import { useState } from "react";
const [open, setOpen] = useState(false);
<Button onClick={() => setOpen(true)}>Edit profile</Button>
<Modal show={open} onClose={() => setOpen(false)} size="md">
<ModalHeader>Edit profile</ModalHeader>
<ModalBody>{/* form fields */}</ModalBody>
<ModalFooter>
<Button color="alternative" onClick={() => setOpen(false)}>Cancel</Button>
<Button onClick={() => setOpen(false)}>Save</Button>
</ModalFooter>
</Modal>
import Button from "@mui/material/Button";
import Dialog from "@mui/material/Dialog";
import DialogTitle from "@mui/material/DialogTitle";
import DialogContent from "@mui/material/DialogContent";
import DialogActions from "@mui/material/DialogActions";
import { useState } from "react";
const [open, setOpen] = useState(false);
<Button variant="contained" onClick={() => setOpen(true)}>Edit profile</Button>
<Dialog open={open} onClose={() => setOpen(false)}>
<DialogTitle>Edit profile</DialogTitle>
<DialogContent>{/* form fields */}</DialogContent>
<DialogActions>
<Button onClick={() => setOpen(false)}>Cancel</Button>
<Button variant="contained" onClick={() => setOpen(false)}>Save</Button>
</DialogActions>
</Dialog>
Les menus ont besoin de la navigation aux flèches, de la recherche par saisie et de rôles corrects. Radix, Headless UI, shadcn/ui et MUI s'en chargent ; la version <details> de daisyUI s'ouvre et se ferme sans JavaScript mais n'ajoute pas la navigation aux flèches.
import {
DropdownMenu, DropdownMenuContent, DropdownMenuItem,
DropdownMenuSeparator, DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
<DropdownMenu>
<DropdownMenuTrigger asChild><Button variant="outline">Options</Button></DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem onSelect={rename}>Rename</DropdownMenuItem>
<DropdownMenuItem onSelect={duplicate}>Duplicate</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive">Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
import { DropdownMenu } from "radix-ui";
<DropdownMenu.Root>
<DropdownMenu.Trigger>Options</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content sideOffset={6} className="rounded-lg bg-white p-1 shadow-lg">
<DropdownMenu.Item onSelect={rename} className="data-[highlighted]:bg-black/5">Rename</DropdownMenu.Item>
<DropdownMenu.Item onSelect={duplicate}>Duplicate</DropdownMenu.Item>
<DropdownMenu.Separator className="my-1 h-px bg-black/10" />
<DropdownMenu.Item disabled>Move</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
<Menu>
<MenuButton>Options</MenuButton>
<MenuItems anchor="bottom start" className="rounded-lg bg-white p-1 shadow-lg">
<MenuItem>
<button onClick={rename} className="block w-full data-focus:bg-black/5">Rename</button>
</MenuItem>
<MenuItem>
<button onClick={duplicate} className="block w-full data-focus:bg-black/5">Duplicate</button>
</MenuItem>
</MenuItems>
</Menu>
<details className="dropdown">
<summary className="btn">Options</summary>
<ul className="menu dropdown-content z-1 w-52 rounded-box bg-base-100 p-2 shadow-sm">
<li><button onClick={rename}>Rename</button></li>
<li><button onClick={duplicate}>Duplicate</button></li>
</ul>
</details>
import { Dropdown, DropdownDivider, DropdownItem } from "flowbite-react";
<Dropdown label="Options" dismissOnClick>
<DropdownItem onClick={rename}>Rename</DropdownItem>
<DropdownItem onClick={duplicate}>Duplicate</DropdownItem>
<DropdownDivider />
<DropdownItem>Delete</DropdownItem>
</Dropdown>
import Button from "@mui/material/Button";
import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
import { useState } from "react";
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
const close = () => setAnchorEl(null);
<Button onClick={(e) => setAnchorEl(e.currentTarget)}>Options</Button>
<Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={close}>
<MenuItem onClick={() => { rename(); close(); }}>Rename</MenuItem>
<MenuItem onClick={() => { duplicate(); close(); }}>Duplicate</MenuItem>
</Menu>
Des onglets corrects exigent role="tablist", role="tab", aria-selected et le passage d'un onglet à l'autre avec les flèches. Dans daisyUI et MUI, vous reliez vous-même les panneaux et les ID ; les autres le font pour vous.
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
<Tabs defaultValue="account">
<TabsList>
<TabsTrigger value="account">Account</TabsTrigger>
<TabsTrigger value="billing">Billing</TabsTrigger>
</TabsList>
<TabsContent value="account">Account settings</TabsContent>
<TabsContent value="billing">Billing settings</TabsContent>
</Tabs>
import { Tabs } from "radix-ui";
<Tabs.Root defaultValue="account">
<Tabs.List aria-label="Settings">
<Tabs.Trigger value="account" className="data-[state=active]:font-semibold">Account</Tabs.Trigger>
<Tabs.Trigger value="billing" className="data-[state=active]:font-semibold">Billing</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="account">Account settings</Tabs.Content>
<Tabs.Content value="billing">Billing settings</Tabs.Content>
</Tabs.Root>
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@headlessui/react";
<TabGroup>
<TabList>
<Tab className="data-selected:font-semibold">Account</Tab>
<Tab className="data-selected:font-semibold">Billing</Tab>
</TabList>
<TabPanels>
<TabPanel>Account settings</TabPanel>
<TabPanel>Billing settings</TabPanel>
</TabPanels>
</TabGroup>
const [tab, setTab] = useState("account");
<div role="tablist" className="tabs tabs-box">
<button role="tab" aria-selected={tab === "account"}
className={"tab " + (tab === "account" ? "tab-active" : "")}
onClick={() => setTab("account")}>Account</button>
<button role="tab" aria-selected={tab === "billing"}
className={"tab " + (tab === "billing" ? "tab-active" : "")}
onClick={() => setTab("billing")}>Billing</button>
</div>
<div role="tabpanel">{tab === "account" ? "Account settings" : "Billing settings"}</div>
import { TabItem, Tabs } from "flowbite-react";
<Tabs aria-label="Settings" variant="underline" onActiveTabChange={(i) => console.log(i)}>
<TabItem active title="Account">Account settings</TabItem>
<TabItem title="Billing">Billing settings</TabItem>
</Tabs>
import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import { useState } from "react";
const [tab, setTab] = useState(0);
<Tabs value={tab} onChange={(_, next) => setTab(next)} aria-label="Settings">
<Tab label="Account" id="tab-0" aria-controls="panel-0" />
<Tab label="Billing" id="tab-1" aria-controls="panel-1" />
</Tabs>
<div role="tabpanel" id={`panel-${tab}`} aria-labelledby={`tab-${tab}`}>
{tab === 0 ? "Account settings" : "Billing settings"}
</div>
Un interrupteur est une commande marche/arrêt annoncée comme role="switch". daisyUI stylise une vraie case à cocher, ce qui conserve le comportement natif des formulaires ; les autres produisent un bouton avec le rôle switch.
import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label";
<div className="flex items-center gap-2">
<Switch id="email-alerts" checked={on} onCheckedChange={setOn} />
<Label htmlFor="email-alerts">Email notifications</Label>
</div>
import { Switch } from "radix-ui";
<Switch.Root id="email-alerts" checked={on} onCheckedChange={setOn}
className="h-6 w-11 rounded-full bg-black/20 data-[state=checked]:bg-blue-500">
<Switch.Thumb className="block h-5 w-5 rounded-full bg-white transition-transform data-[state=checked]:translate-x-5" />
</Switch.Root>
<label htmlFor="email-alerts">Email notifications</label>
import { Field, Label, Switch } from "@headlessui/react";
<Field className="flex items-center gap-2">
<Switch checked={on} onChange={setOn}
className="group h-6 w-11 rounded-full bg-black/20 data-checked:bg-blue-500">
<span className="block h-5 w-5 rounded-full bg-white transition group-data-checked:translate-x-5" />
</Switch>
<Label>Email notifications</Label>
</Field>
<label className="flex items-center gap-2">
<input type="checkbox" className="toggle toggle-primary"
checked={on} onChange={(e) => setOn(e.target.checked)} />
Email notifications
</label>
import { ToggleSwitch } from "flowbite-react";
<ToggleSwitch checked={on} onChange={setOn} label="Email notifications" />
import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch";
<FormControlLabel
control={<Switch checked={on} onChange={(e) => setOn(e.target.checked)} />}
label="Email notifications"
/>
C'est sur les toasts que les bibliothèques diffèrent le plus. Certaines vous donnent un composant que vous pilotez avec un état, d'autres une fonction à appeler de n'importe où, et Headless UI n'en propose pas du tout.
// after: npx shadcn add sonner
import { Toaster } from "@/components/ui/sonner";
import { toast } from "sonner";
// once, near the root of your app
<Toaster />
// anywhere
<Button onClick={() => toast("Changes saved", {
description: "Your profile is up to date.",
action: { label: "Undo", onClick: undo },
})}>
Save
</Button>
import { Toast } from "radix-ui";
<Toast.Provider duration={4000}>
<button onClick={() => setOpen(true)}>Save</button>
<Toast.Root open={open} onOpenChange={setOpen} className="rounded-lg bg-white p-3 shadow-lg">
<Toast.Title>Changes saved</Toast.Title>
<Toast.Description>Your profile is up to date.</Toast.Description>
<Toast.Action altText="Undo the save" onClick={undo}>Undo</Toast.Action>
</Toast.Root>
<Toast.Viewport className="fixed bottom-4 right-4 w-80" />
</Toast.Provider>
// Headless UI has no toast component. Common choices:
// pair it with a small toast package, or build one from
// Transition plus an aria-live region:
import { Transition } from "@headlessui/react";
<div aria-live="polite" className="fixed bottom-4 right-4">
<Transition show={open}>
<div className="rounded-lg bg-white p-3 shadow-lg transition data-closed:opacity-0">
Changes saved
</div>
</Transition>
</div>
{/* daisyUI positions and stacks; showing and hiding is your state */}
{open && (
<div className="toast toast-end toast-bottom" role="status">
<div className="alert alert-success">
<span>Changes saved</span>
</div>
</div>
)}
import { Toast, ToastToggle } from "flowbite-react";
{open && (
<Toast className="fixed bottom-4 right-4">
<div className="text-sm font-normal">Changes saved</div>
<ToastToggle onDismiss={() => setOpen(false)} />
</Toast>
)}
import Snackbar from "@mui/material/Snackbar";
import Button from "@mui/material/Button";
<Snackbar
open={open}
autoHideDuration={4000}
onClose={() => setOpen(false)}
message="Changes saved"
action={<Button size="small" onClick={undo}>Undo</Button>}
/>
| Projet | Bon choix | Pourquoi |
|---|---|---|
| Site vitrine ou landing page en Tailwind | daisyUI, ou Tailwind seul | Des pages surtout statiques ont besoin de boutons, de cartes et d'un menu, pas d'une couche de composants JavaScript. Les classes de daisyUI fonctionnent aussi dans des pages sans React. |
| Produit SaaS avec sa propre identité | shadcn/ui, ou Radix / Headless UI stylés par vos soins | Vous obtenez un comportement accessible sans hériter de l'apparence d'un autre produit, et vous pouvez coller exactement à votre design system. |
| Créer votre propre design system ou bibliothèque de composants | Radix Primitives (ou Base UI), Headless UI | Des primitives sans style sont justement la couche au-dessus de laquelle vous voulez maîtriser les styles. shadcn/ui est lui-même un exemple de cette approche. |
| Outil interne, back-office, tableau de bord | MUI, ou shadcn/ui | L'étendue de MUI (tableaux, sélecteurs de date via MUI X, formulaires) fait gagner le plus de temps quand livrer compte plus que l'apparence. shadcn/ui si vous utilisez déjà Tailwind. |
| Prototype ou hackathon sous Tailwind | Flowbite React ou daisyUI | Des composants à l'aspect fini avec très peu de travail de style. Prévoyez de revoir ce choix si le prototype devient un produit. |
| Équipe qui utilise déjà Material Design (ou une interface de style Android) | MUI | Il implémente directement Material Design, si bien que designers et développeurs partagent le même vocabulaire. |
| Exigences d'accessibilité strictes (secteur public, grandes entreprises) | Radix Primitives, Headless UI, MUI, ou shadcn/ui sur ces bases | Le comportement au clavier et la gestion du focus sont intégrés et documentés. Quel que soit votre choix, testez quand même au clavier et avec un lecteur d'écran ; aucune bibliothèque ne rend une page accessible à elle seule. |
Pour les hooks sur lesquels reposent ces composants, consultez l'aide-mémoire des hooks React. Pour les classes utilitaires utilisées dans les extraits, voyez l'aide-mémoire Tailwind, et pour les états hover, focus et désactivé dont chaque bouton a besoin, les états de bouton en CSS. Vous pouvez aussi parcourir dans notre galerie de composants des modales, des boutons et des éléments de navigation React et Tailwind prêts à l'emploi.
Vérifié en octobre 2026 : la documentation et le changelog de shadcn/ui (ui.shadcn.com), le dépôt GitHub shadcn-ui/ui ; la documentation de Radix Primitives, y compris sa présentation de l'accessibilité (radix-ui.com/primitives) ; Headless UI (headlessui.com) ; les pages d'installation, de mise à niveau et de composants de daisyUI (daisyui.com) ; la documentation de MUI Material UI (mui.com) ; et les fiches du registre npm de shadcn, radix-ui, @headlessui/react, daisyui, flowbite-react et @mui/material pour les versions, les licences et les peer dependencies.
Pas au sens habituel. Sa propre documentation explique que ce n'est pas une bibliothèque de composants mais une façon de construire la vôtre : une CLI copie le code source des composants dans votre projet, et c'est vous qui modifiez et maintenez ce code. Ses parties interactives reposent sur une bibliothèque headless, Base UI par défaut pour les nouveaux projets depuis juillet 2026, Radix restant pris en charge.
Les deux sont des composants React accessibles et sans style. Radix Primitives propose un ensemble plus large, avec notamment toast, tooltip, slider et toolbar, et prend en charge React 16.8 à 19. Headless UI vient de l'équipe de Tailwind CSS, propose un ensemble plus restreint sans toast ni tooltip, et exige React 18 ou 19.
Oui. daisyUI est un plugin Tailwind CSS qui ajoute des noms de classes comme btn, modal et tabs, que vous utilisez en JSX comme n'importe quelle autre classe Tailwind. Il ne fournit pas de composants React, donc l'état, comme l'ouverture d'une modale ou le changement d'onglet, est géré dans votre propre code React.
Oui. shadcn/ui, Radix Primitives, Headless UI, daisyUI, flowbite-react et le paquet principal Material UI de MUI sont tous sous licence MIT au moment de la rédaction (octobre 2026). Les composants MUI X Pro et Premium et les blocs Pro de Flowbite sont des produits commerciaux distincts.
Radix Primitives, Headless UI et MUI intègrent la navigation au clavier et la gestion du focus dans leurs composants, et shadcn/ui en hérite par sa couche de primitives. daisyUI s'appuie sur les éléments HTML natifs, donc vous ajoutez vous-même ARIA et le comportement clavier. Quel que soit votre choix, testez au clavier et avec un lecteur d'écran.
Oui, mais ils stylent différemment : MUI utilise Emotion et un thème JavaScript, Tailwind des classes utilitaires. Ils fonctionnent côte à côte, mais vous devez synchroniser vous-même les couleurs et les espacements entre les deux thèmes. Les équipes qui veulent tout en Tailwind choisissent plutôt shadcn/ui ou une bibliothèque headless.
En direct : Radix Primitives {title} (bibliothèque sans style, nos classes Tailwind)
Modifier le profil
Appuyez sur Tab : le focus reste à l'intérieur. Appuyez sur Escape ou cliquez à l'extérieur pour fermer.
Nom affiché
Annuler
Enregistrer
Fermer
rien pour l'instant
Options
Renommer
Dupliquer
Déplacer (désactivé)
Afficher les fichiers masqués
Essayez les flèches et la recherche par saisie. Dernier choix : {last}
Paramètres
Compte
Facturation
Équipe
Panneau des paramètres du compte. Utilisez les flèches gauche et droite sur les onglets.
Panneau Facturation.
Panneau Équipe.
Notifications par e-mail : activées
Notifications par e-mail : désactivées
Enregistrer les modifications
Modifications enregistrées
Enregistré {n} fois dans cette démo.
Enregistré {n} fois dans cette démo.
Annuler l'enregistrement
Annuler
Commencer gratuitement · Sans carte bancaire