Bibliotecas de componentes de UI para React comparadas: shadcn/ui, Radix, Headless UI, daisyUI, Flowbite e MUI

shadcn/ui, Radix, Headless UI, daisyUI, Flowbite e MUI diferem principalmente no estilo: Radix e Headless UI não têm estilo, daisyUI e Flowbite são Tailwind pré-estilizado, o MUI é Material Design e o shadcn/ui copia código estilizado para o seu projeto. Aqui estão os mesmos seis componentes em cada uma, e qual escolher.

A versão curta

Essas seis bibliotecas resolvem problemas diferentes, e é por isso que a pergunta "qual é a melhor" não tem uma resposta única. Duas delas (Radix Primitives e Headless UI) entregam comportamento e acessibilidade sem nenhum estilo. Duas (daisyUI e Flowbite) entregam um visual Tailwind pronto. O MUI entrega um design system completo, com o próprio motor de estilos. O shadcn/ui nem é um pacote: ele copia para o seu projeto o código-fonte de componentes já estilizados, construídos sobre uma camada headless.

Todos os fatos abaixo (versões, licenças, requisitos) foram conferidos na documentação oficial, no repositório do GitHub e na página do npm de cada projeto em outubro de 2026. As versões mudam rápido, então considere os números como "no momento em que este texto foi escrito".

Tabela comparativa

shadcn/uiRadix PrimitivesHeadless UIdaisyUIFlowbite ReactMUI (Material UI)
O que você instalauma CLI que copia o código-fonte dos componentes para o seu repositóriopacotes npm (radix-ui ou um por componente)@headlessui/reactplugin Tailwind daisyuiflowbite-react@mui/material mais Emotion
Modelo de estiloclasses Tailwind em código que é seu; tema via variáveis CSSsem estilo; você adiciona as classes, estado via atributos data-statesem estilo; estado via atributos data-*, pensado para Tailwindclasses CSS de componente (btn, modal) e temascomponentes Tailwind pré-estilizados, com suporte a temasMaterial Design, CSS-in-JS (prop sx, objeto de tema)
Versão (out. 2026)CLI shadcn 4.x (sem pacote de runtime)radix-ui 1.8; pacotes individuais 1.x e 2.x2.25.x0.12 (pre-1.0)9.x
LicençaMITMITMITMITMITMIT (os pagos MUI X Pro/Premium são à parte)
Exige Reactsim16.8 a 1918 ou 19não (HTML e CSS puros)18 ou 1917, 18 ou 19
Exige Tailwindsim (v4 na documentação atual)nãonão, mas foi feito para elesim, Tailwind 4 para o daisyUI 5sim, Tailwind 3 ou 4não
Abordagem de acessibilidadeherda da sua camada de primitivos (Base UI ou Radix)segue as WAI-ARIA Authoring Practices; teclado e foco já incluídosdescrito como "totalmente acessível"; teclado e foco já incluídosdepende de elementos nativos (<dialog>, <details>); você adiciona ARIA e comportamentocomponentes React com posicionamento via Floating UI; teste o uso por teclado você mesmoARIA e suporte a teclado em cada componente, documentados componente a componente
Tem toastsim, via Sonnersim (Toast)nãosim (só a classe de layout)sim (Toast)sim (Snackbar)

Deixamos o tamanho do bundle fora da tabela de propósito. Os números dependem muito de quais componentes você importa, de o seu bundler fazer tree-shaking deles ou não e (no caso do daisyUI) de quanto CSS o Tailwind gera para as classes que você usa. Um único número em "kB" por biblioteca mais confundiria do que ajudaria. Meça o seu próprio build: rode o analisador do seu bundler antes e depois de adicionar os componentes de que você realmente precisa.

Cada biblioteca em detalhe

shadcn/ui

O que é. O shadcn/ui se descreve como "não uma biblioteca de componentes", e sim "como você constrói a sua biblioteca de componentes". Você roda a CLI dele (npx shadcn init, depois npx shadcn add button) e ela escreve o código-fonte do componente no seu projeto. A partir daí o código é seu para editar; não existe pacote de runtime do shadcn para atualizar.

Estilo. Classes do Tailwind CSS dentro dos arquivos copiados, com cores e raios como variáveis CSS, para que você troque o tema em um só lugar. As variantes costumam ser definidas com class-variance-authority.

Por baixo dos panos. As partes interativas ficam sobre uma biblioteca headless. Segundo o changelog do shadcn/ui, desde julho de 2026 os projetos novos usam Base UI por padrão, e o Radix "não está sendo descontinuado": os dois são suportados. O toast dele é feito com o Sonner, um pacote de toast open source separado.

Licença e versão. MIT (repositório do GitHub shadcn-ui/ui). O pacote da CLI, shadcn, estava na 4.x no npm no momento em que este texto foi escrito.

Atenção. Como o código é seu, a manutenção também é. Correções feitas na origem não chegam automaticamente às suas cópias, e projetos diferentes vão se distanciando com o tempo.

Radix Primitives

O que é. Um conjunto de componentes React acessíveis e sem estilo (dialog, menu suspenso, abas, switch, toast, tooltip, popover e outros). Você pode instalar tudo pelo pacote único radix-ui (1.8 no momento em que este texto foi escrito) ou um pacote por componente, como @radix-ui/react-dialog.

Estilo. Nenhum por padrão. Cada parte renderiza um elemento simples que você pode estilizar com qualquer método. O estado é exposto como atributos data, por exemplo data-state="open" ou data-highlighted, que combinam bem com as variantes data-[state=open]: do Tailwind.

Acessibilidade. A documentação do Radix diz que os primitivos seguem as WAI-ARIA Authoring Practices, oferecem suporte a teclado e gerenciam o foco (por exemplo, prendendo o foco dentro de um diálogo aberto e devolvendo-o ao fechar).

Licença e requisitos. MIT. A faixa de peer dependency no npm cobre do React 16.8 ao 19.

Atenção. Você escreve todos os estilos. O Radix não traz um primitivo Button: use um <button> nativo, ou o utilitário Slot para mesclar props no seu próprio elemento.

Headless UI

O que é. Componentes sem estilo feitos pela equipe do Tailwind CSS. O pacote React, @headlessui/react (2.2 no momento em que este texto foi escrito), inclui Button, Checkbox, Combobox, Dialog, Disclosure, Fieldset, Input, Listbox, Menu (dropdown), Popover, Radio Group, Select, Switch, Tabs, Textarea e Transition.

Estilo. Sem estilo. Os componentes expõem o estado por atributos data (data-open, data-checked, data-selected, data-focus), então você os estiliza com variantes do Tailwind como data-checked:bg-blue-600 (sintaxe do Tailwind 4) ou data-[checked]:bg-blue-600 (Tailwind 3).

Acessibilidade. O site descreve os componentes como "completamente sem estilo, totalmente acessíveis". Navegação por teclado, foco preso nos diálogos e a ligação de ARIA já vêm incluídos.

Licença e requisitos. MIT. React 18 ou 19 (peer dependency no npm).

Atenção. O conjunto é menor que o do Radix: não há toast, tooltip nem slider, então você vai precisar de outro pacote para eles.

daisyUI

O que é. Um plugin do Tailwind CSS que adiciona nomes de classe de componentes como btn, modal, dropdown, tabs, toggle e toast, além de um conjunto de temas de cores. Ele entrega CSS, não componentes React, então funciona com qualquer framework ou sem nenhum.

Estilo. Classes semânticas com modificadores (btn btn-primary btn-sm) que você ainda pode combinar com os utilitários normais do Tailwind. Os temas são trocados por variáveis CSS.

Requisitos. O daisyUI 5 (lançado em fevereiro de 2025, na 5.x no momento em que este texto foi escrito) é feito para o Tailwind CSS 4; o guia de atualização manda atualizar o Tailwind primeiro. Você o instala com uma única linha @plugin "daisyui"; no seu CSS.

Acessibilidade. Como é só CSS, o comportamento vem do HTML nativo: o modal usa o elemento <dialog> (Escape fecha e o navegador cuida do foco), os dropdowns usam <details> ou a API de popover. Qualquer coisa além disso (ARIA nas abas, anúncio dos toasts) fica por sua conta.

Licença. MIT.

Atenção. No React, você mesmo conecta o estado (por exemplo, chamando showModal() por uma ref). Não há suporte às setas do teclado em menus ou abas, a menos que você o adicione.

Flowbite React

O que é. Componentes React (flowbite-react) estilizados com Tailwind, do projeto Flowbite. Inclui Button, Modal, Dropdown, Tabs, ToggleSwitch, Toast, Navbar, Sidebar, Datepicker e outros.

Estilo. Pré-estilizados com classes Tailwind, personalizados por um objeto de tema e por uma prop theme em cada componente.

Requisitos e versão. Versão 0.12 no momento em que este texto foi escrito: ainda não chegou à 1.0, então espere mudanças incompatíveis entre versões minor. As peer dependencies no npm são React 18 ou 19 e Tailwind CSS 3 ou 4. Ele usa o Floating UI para posicionar dropdowns e tooltips.

Acessibilidade. Os componentes renderizam os roles esperados para os padrões comuns, mas não encontramos uma declaração de acessibilidade publicada comparável à do Radix. Teste o comportamento com teclado e leitor de tela nos componentes de que você depende.

Licença. MIT (a biblioteca open source; o Flowbite também vende à parte blocos Pro e arquivos de design).

MUI (Material UI)

O que é. Nas palavras do próprio projeto, "uma biblioteca open source de componentes React que implementa o Material Design do Google." É o maior conjunto desta comparação, com componentes de exibição de dados, entradas, navegação, layout e feedback.

Estilo. CSS-in-JS. Por padrão usa Emotion (@emotion/react e @emotion/styled são peer dependencies do @mui/material 9). Você define o tema com createTheme e estiliza casos pontuais com a prop sx. Ele pode conviver com o Tailwind, mas os dois sistemas de tema não compartilham tokens, a menos que você os conecte.

Versão e licença. Versão 9 (a 9.0.0 foi publicada em abril de 2026). A biblioteca principal é MIT. Os recursos avançados de data grid, datas e gráficos do MUI X Pro e Premium têm licenças comerciais.

Acessibilidade. Os componentes vêm com atributos ARIA e suporte a teclado, e as páginas de componentes na documentação do MUI descrevem o comportamento de acessibilidade de cada um.

Atenção. Fugir do visual Material exige um trabalho real de tema, e o CSS-in-JS precisa de configuração extra com React Server Components.

Os mesmos seis componentes em cada biblioteca

Abaixo, o mesmo botão, diálogo, menu suspenso, abas, switch e toast estão escritos seis vezes, uma na API documentada de cada biblioteca. Cada trecho é código original escrito para esta página (não copiado da documentação das bibliotecas), usa a versão major atual de cada biblioteca e foi reduzido às partes que mudam. Versões ao vivo e clicáveis aparecem apenas para o Radix Primitives, porque o Radix é a biblioteca instalada neste site. Elas usam as nossas próprias classes Tailwind, já que o Radix não traz estilos. Não mostramos versões ao vivo das outras cinco bibliotecas, então nada aqui imita o visual delas.

Botão

A maioria das bibliotecas traz um botão com variantes. Repare como o Radix e o Headless UI deixam o visual com você, enquanto o daisyUI expressa as variantes como classes e as demais como props.

Botão: shadcn/ui

// 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>

Botão: Radix Primitives

// 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} />;
}

Botão: Headless UI

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>

Botão: daisyUI

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

Botão: Flowbite React

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>

Botão: MUI

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>

Diálogo (modal)

O diálogo é onde o trabalho de acessibilidade se esconde: foco preso, Escape para fechar, devolver o foco ao gatilho, rótulos. As bibliotecas headless e o MUI fazem isso em JavaScript; o daisyUI se apoia no elemento nativo <dialog>, que trata o Escape e o foco no navegador.

Diálogo (modal): shadcn/ui

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>

Diálogo (modal): Radix Primitives

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>

Diálogo (modal): Headless UI

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>

Diálogo (modal): daisyUI

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>

Diálogo (modal): Flowbite React

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>

Diálogo (modal): MUI

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>

Menu suspenso

Menus precisam de navegação pelas setas, busca ao digitar e roles corretos. Radix, Headless UI, shadcn/ui e MUI cuidam disso; a versão do daisyUI com <details> abre e fecha sem JavaScript, mas não adiciona navegação pelas setas.

Menu suspenso: shadcn/ui

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>

Menu suspenso: Radix Primitives

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>

Menu suspenso: Headless UI

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>

Menu suspenso: daisyUI

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

Menu suspenso: Flowbite React

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>

Menu suspenso: MUI

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>

Abas

Abas corretas precisam de role="tablist", role="tab", aria-selected e movimento entre abas pelas setas. No daisyUI e no MUI você mesmo liga os painéis e os IDs; as outras fazem essa ligação por você.

Abas: shadcn/ui

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>

Abas: Radix Primitives

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>

Abas: Headless UI

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>

Abas: daisyUI

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>

Abas: Flowbite React

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>

Abas: MUI

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>

Switch (alternador)

Um switch é um controle de liga/desliga anunciado como role="switch". O daisyUI estiliza um checkbox de verdade, o que mantém o comportamento nativo de formulário; as outras renderizam um botão com o role switch.

Switch (alternador): shadcn/ui

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>

Switch (alternador): Radix Primitives

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>

Switch (alternador): Headless UI

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>

Switch (alternador): daisyUI

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

Switch (alternador): Flowbite React

import { ToggleSwitch } from "flowbite-react";

<ToggleSwitch checked={on} onChange={setOn} label="Email notifications" />

Switch (alternador): MUI

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"
/>

Toast (notificação)

É nos toasts que as bibliotecas mais diferem. Algumas dão um componente que você controla com estado, outras dão uma função que você chama de qualquer lugar, e o Headless UI simplesmente não tem.

Toast (notificação): shadcn/ui

// 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>

Toast (notificação): Radix Primitives

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>

Toast (notificação): Headless UI

// 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>

Toast (notificação): daisyUI

{/* 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>
)}

Toast (notificação): Flowbite React

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>
)}

Toast (notificação): MUI

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>}
/>

Qual escolher por tipo de projeto

ProjetoBoa escolhaPor quê
Site de marketing ou landing page em TailwinddaisyUI, ou Tailwind puroPáginas quase estáticas precisam de botões, cards e um menu, não de uma camada de componentes em JavaScript. As classes do daisyUI também funcionam em páginas sem React.
Produto SaaS com marca própriashadcn/ui, ou Radix / Headless UI estilizados por vocêVocê ganha comportamento acessível sem herdar o visual de outro produto e consegue seguir o seu design system com exatidão.
Construir o seu próprio design system ou biblioteca de componentesRadix Primitives (ou Base UI), Headless UIPrimitivos sem estilo são a camada sobre a qual você quer ser dono dos estilos. O próprio shadcn/ui é um exemplo dessa abordagem.
Ferramenta interna, painel admin, dashboardMUI, ou shadcn/uiA amplitude do MUI (tabelas, seletores de data via MUI X, formulários) é o que mais economiza tempo quando entregar importa mais do que o visual. shadcn/ui se você já usa Tailwind.
Protótipo ou hackathon em TailwindFlowbite React ou daisyUIComponentes com cara de prontos e pouquíssimo trabalho de estilo. Planeje revisar se o protótipo virar produto.
Equipe que já usa Material Design (ou interface no estilo Android)MUIEle implementa o Material Design diretamente, então designers e desenvolvedores compartilham o mesmo vocabulário.
Requisitos rígidos de acessibilidade (setor público, grandes empresas)Radix Primitives, Headless UI, MUI, ou shadcn/ui sobre elesO comportamento de teclado e foco já vem incluído e documentado. Seja qual for a escolha, teste mesmo assim com teclado e leitor de tela; nenhuma biblioteca torna uma página acessível sozinha.

Perguntas para responder antes de escolher

Para os hooks em que esses componentes se baseiam, veja a cola de React hooks. Para as classes utilitárias usadas nos trechos, veja a cola de Tailwind e, para os estados de hover, foco e desativado de que todo botão precisa, estados de botão em CSS. Você também pode explorar modais, botões e peças de navegação prontos em React e Tailwind na nossa galeria de componentes.

Fontes

Conferido em outubro de 2026: a documentação e o changelog do shadcn/ui (ui.shadcn.com), o repositório shadcn-ui/ui no GitHub; a documentação do Radix Primitives, incluindo a visão geral de acessibilidade (radix-ui.com/primitives); Headless UI (headlessui.com); as páginas de instalação, atualização e componentes do daisyUI (daisyui.com); a documentação do MUI Material UI (mui.com); e as páginas do registro npm de shadcn, radix-ui, @headlessui/react, daisyui, flowbite-react e @mui/material para versões, licenças e peer dependencies.

Perguntas frequentes

O shadcn/ui é uma biblioteca de componentes?

Não no sentido habitual. A própria documentação diz que ele não é uma biblioteca de componentes, e sim uma forma de construir a sua: uma CLI copia o código-fonte dos componentes para o seu projeto, e você edita e mantém esse código. As partes interativas são feitas sobre uma biblioteca headless, Base UI por padrão em projetos novos desde julho de 2026, com o Radix ainda suportado.

Qual é a diferença entre Radix e Headless UI?

Os dois são componentes React acessíveis e sem estilo. O Radix Primitives tem um conjunto maior, incluindo toast, tooltip, slider e toolbar, e suporta do React 16.8 ao 19. O Headless UI vem da equipe do Tailwind CSS, tem um conjunto menor, sem toast nem tooltip, e exige React 18 ou 19.

O daisyUI funciona com React?

Sim. O daisyUI é um plugin do Tailwind CSS que adiciona nomes de classe como btn, modal e tabs, então você usa essas classes no JSX como qualquer outra classe do Tailwind. Ele não traz componentes React, então estados como abrir um modal ou trocar de aba ficam no seu próprio código React.

Essas bibliotecas são gratuitas para uso comercial?

Sim. shadcn/ui, Radix Primitives, Headless UI, daisyUI, flowbite-react e o pacote principal Material UI do MUI têm licença MIT no momento em que este texto foi escrito (outubro de 2026). Os componentes MUI X Pro e Premium e os blocos Pro do Flowbite são produtos comerciais à parte.

Qual biblioteca de UI para React é melhor em acessibilidade?

Radix Primitives, Headless UI e MUI incluem navegação por teclado e gerenciamento de foco nos componentes, e o shadcn/ui herda isso da sua camada de primitivos. O daisyUI depende de elementos HTML nativos, então você mesmo adiciona ARIA e comportamento de teclado. Seja qual for a escolha, teste com teclado e leitor de tela.

Posso usar o MUI com Tailwind CSS?

Pode, mas eles estilizam de formas diferentes: o MUI usa Emotion e um tema em JavaScript, o Tailwind usa classes utilitárias. Eles funcionam lado a lado, mas você mesmo precisa manter cores e espaçamentos sincronizados entre os dois temas. Equipes que querem tudo em Tailwind normalmente escolhem o shadcn/ui ou uma biblioteca headless.

Ao vivo: Radix Primitives {title} (biblioteca sem estilo, nossas classes Tailwind)

Editar perfil

Pressione Tab: o foco fica dentro. Pressione Escape ou clique fora para fechar.

Nome de exibição

Cancelar

Salvar

Fechar

nada ainda

Opções

Renomear

Duplicar

Mover (desativado)

Mostrar arquivos ocultos

Experimente as setas e a busca ao digitar. Última escolha: {last}

Configurações

Conta

Cobrança

Equipe

Painel de configurações da conta. Use as setas Left e Right nas abas.

Painel de cobrança.

Painel da equipe.

Notificações por e-mail: ativadas

Notificações por e-mail: desativadas

Salvar alterações

Alterações salvas

Salvo {n} vez nesta demonstração.

Salvo {n} vezes nesta demonstração.

Desfazer o salvamento

Desfazer

Comece grátis · Sem cartão de crédito

Relacionados: Guias

Produto

Para quem é

Recursos

Recursos

Empresa

Mapa do site