React UIコンポーネントライブラリ比較:shadcn/ui、Radix、Headless UI、daisyUI、Flowbite、MUI

shadcn/ui、Radix、Headless UI、daisyUI、Flowbite、MUIの主な違いはスタイリングです。RadixとHeadless UIはスタイルなし、daisyUIとFlowbiteはスタイル済みのTailwind、MUIはMaterial Design、shadcn/uiはスタイル付きのコードをプロジェクトにコピーします。同じ6つのコンポーネントを各ライブラリで書き比べ、選び方も紹介します。

要点だけ

この6つのライブラリはそれぞれ別の問題を解決するものなので、「どれが一番か」に唯一の答えはありません。2つ(Radix PrimitivesとHeadless UI)は、スタイルなしで動作とアクセシビリティを提供します。2つ(daisyUIとFlowbite)は、完成したTailwindの見た目を提供します。MUIは独自のスタイリングエンジンを備えた完全なデザインシステムです。shadcn/uiはそもそもパッケージではありません。スタイル付きのコンポーネントのソースをプロジェクトにコピーするもので、その下にはヘッドレスの層があります。

以下の事実(バージョン、ライセンス、動作要件)はすべて、2026年10月に各プロジェクトの公式ドキュメント、GitHubリポジトリ、npmの掲載情報と照らし合わせて確認しました。バージョンはすぐに変わるので、数値は「執筆時点」のものとして扱ってください。

比較表

shadcn/uiRadix PrimitivesHeadless UIdaisyUIFlowbite ReactMUI (Material UI)
インストールするものコンポーネントのソースをリポジトリにコピーするCLInpmパッケージ(radix-ui、またはコンポーネントごとに1つ)@headlessui/reactdaisyui(Tailwindプラグイン)flowbite-react@mui/materialとEmotion
スタイリングの方式自分が所有するコード内のTailwindクラス。テーマはCSS変数でスタイルなし。クラスは自分で付け、状態はdata-state属性でスタイルなし。状態はdata-*属性で、Tailwind向けに設計CSSのコンポーネントクラス(btn、modal)とテーマスタイル済みのTailwindコンポーネント、テーマ変更可能Material Design、CSS-in-JS(sxプロパティ、テーマオブジェクト)
バージョン(2026年10月)CLIのshadcn 4.x(ランタイムのパッケージはなし)radix-ui 1.8。個別パッケージは1.xと2.x2.25.x0.12(1.0未満)9.x
ライセンスMITMITMITMITMITMIT(有料のMUI X Pro/Premiumは別扱い)
Reactの要件必要16.8〜1918または19不要(素のHTMLとCSS)18または1917、18、19のいずれか
Tailwindの要件必要(現在のドキュメントではv4)不要不要だが、前提として設計必要。daisyUI 5にはTailwind 4必要。Tailwind 3または4不要
アクセシビリティへの取り組み土台のプリミティブ層(Base UIまたはRadix)から引き継ぐWAI-ARIA Authoring Practicesに準拠。キーボードとフォーカスの処理を内蔵「完全にアクセシブル」とうたう。キーボードとフォーカスの処理を内蔵ネイティブ要素(<dialog>、<details>)に頼る。ARIAと動作は自分で追加Floating UIで位置決めするReactコンポーネント。キーボード操作は自分でテストコンポーネントごとにARIAとキーボード処理があり、それぞれ文書化されている
トーストの有無あり(Sonner経由)あり(Toast)なしあり(レイアウト用クラスのみ)あり(Toast)あり(Snackbar)

バンドルサイズは、意図的に表から外しました。その数値は、どのコンポーネントをインポートするか、バンドラーがツリーシェイキングするかどうか、そして(daisyUIの場合)使うクラスに対してTailwindがどれだけのCSSを生成するかに大きく左右されます。ライブラリごとに「kB」の数字を1つ示しても、役に立つより誤解を招くほうが大きいでしょう。自分のビルドで測ってください。実際に必要なコンポーネントを追加する前と後で、バンドラーの分析ツールを実行しましょう。

各ライブラリの詳細

shadcn/ui

概要。shadcn/uiは自らを「コンポーネントライブラリではない」と言い、「コンポーネントライブラリを作るための方法」だと説明しています。CLIを実行すると(npx shadcn init、続いてnpx shadcn add button)、コンポーネントのソースがプロジェクトに書き込まれます。それ以降、コードは自由に編集でき、アップグレードすべきshadcnのランタイムパッケージはありません。

スタイリング。コピーされたファイル内のTailwind CSSクラスで、色や角の丸みはCSS変数になっているため、1か所でテーマを変えられます。バリエーションは通常class-variance-authorityで定義します。

内部の仕組み。操作できる部分は、ヘッドレスライブラリの上に作られています。shadcn/uiの変更履歴によれば、2026年7月以降、新しいプロジェクトの既定はBase UIで、Radixは「非推奨になるわけではない」とされ、両方がサポートされています。トーストは、別のオープンソースのトーストパッケージであるSonnerで作られています。

ライセンスとバージョン。MIT(GitHubリポジトリshadcn-ui/ui)。執筆時点で、CLIパッケージshadcnはnpm上で4.xでした。

注意点。コードを所有するということは、保守も自分で担うということです。元のプロジェクトでの修正は手元のコピーに自動では届かず、プロジェクトごとの差も時間とともに広がっていきます。

Radix Primitives

概要。スタイルのない、アクセシブルなReactコンポーネント群です(ダイアログ、ドロップダウンメニュー、タブ、スイッチ、トースト、ツールチップ、ポップオーバーなど)。単一のradix-uiパッケージ(執筆時点で1.8)からすべてをインストールすることも、@radix-ui/react-dialogのようにコンポーネントごとのパッケージを入れることもできます。

スタイリング。既定ではありません。各パーツは素の要素を描画し、どんな方法でもスタイルを付けられます。状態はdata-state="open"やdata-highlightedのようなデータ属性として公開され、Tailwindのdata-[state=open]:バリアントと相性が良いです。

アクセシビリティ。Radixのドキュメントによれば、プリミティブはWAI-ARIA Authoring Practicesに準拠し、キーボード操作に対応し、フォーカスを管理します(たとえば、開いたダイアログの中にフォーカスを閉じ込め、閉じたときに元へ戻します)。

ライセンスと動作要件。MIT。npmのピア依存関係の範囲は、React 16.8から19までです。

注意点。スタイルはすべて自分で書きます。Radixにはボタンのプリミティブがありません。ネイティブの<button>を使うか、Slotユーティリティでプロパティを自分の要素に統合しましょう。

Headless UI

概要。Tailwind CSSチームによる、スタイルのないコンポーネントです。Reactパッケージ@headlessui/react(執筆時点で2.2)には、Button、Checkbox、Combobox、Dialog、Disclosure、Fieldset、Input、Listbox、Menu(ドロップダウン)、Popover、Radio Group、Select、Switch、Tabs、Textarea、Transitionが含まれます。

スタイリング。スタイルなしです。コンポーネントはデータ属性(data-open、data-checked、data-selected、data-focus)で状態を公開するので、data-checked:bg-blue-600(Tailwind 4の構文)やdata-[checked]:bg-blue-600(Tailwind 3)のようなTailwindのバリアントでスタイルを付けます。

アクセシビリティ。公式サイトではコンポーネントを「完全にスタイルなしで、完全にアクセシブル」と説明しています。キーボード操作、ダイアログ内でのフォーカスの閉じ込め、ARIAの紐付けが組み込まれています。

ライセンスと動作要件。MIT。React 18または19(npmのピア依存関係)。

注意点。コンポーネントの数はRadixより少なく、トースト、ツールチップ、スライダーがないため、それらには別のパッケージが必要です。

daisyUI

概要。btn、modal、dropdown、tabs、toggle、toastといったコンポーネントのクラス名と、一連のカラーテーマを追加するTailwind CSSプラグインです。提供するのはReactコンポーネントではなくCSSなので、どのフレームワークでも、フレームワークなしでも動きます。

スタイリング。修飾子付きの意味のあるクラス(btn btn-primary btn-sm)で、通常のTailwindユーティリティとも組み合わせられます。テーマはCSS変数で切り替わります。

動作要件。daisyUI 5(2025年2月に初リリース、執筆時点で5.x)はTailwind CSS 4を対象としており、アップグレードガイドでは最初にTailwindを更新するよう案内されています。インストールは、CSSに@plugin "daisyui";の1行を書くだけです。

アクセシビリティ。CSSだけなので、動作はネイティブのHTMLに由来します。モーダルは<dialog>要素を使い(Escapeで閉じ、フォーカスはブラウザーが処理します)、ドロップダウンは<details>かポップオーバーAPIを使います。それ以上のこと(タブのARIA、トーストの読み上げ)は自分で行う必要があります。

ライセンス。MIT。

注意点。Reactでは状態を自分でつなぎ込みます(たとえば、ref経由でshowModal()を呼ぶ)。自分で追加しない限り、メニューやタブで矢印キーを使った操作はできません。

Flowbite React

概要。Flowbiteプロジェクトによる、Tailwindでスタイリングされたコンポーネント(flowbite-react)です。Button、Modal、Dropdown、Tabs、ToggleSwitch、Toast、Navbar、Sidebar、Datepickerなどが含まれます。

スタイリング。Tailwindのクラスでスタイル済みで、テーマオブジェクトと各コンポーネントのthemeプロパティでカスタマイズします。

動作要件とバージョン。執筆時点でバージョン0.12です。1.0に達していないので、マイナーバージョン間でも互換性のない変更があると考えておきましょう。npmのピア依存関係は、React 18または19と、Tailwind CSS 3または4です。ドロップダウンとツールチップの位置決めにはFloating UIを使っています。

アクセシビリティ。よくあるパターンについては、コンポーネントが想定どおりのロールを描画しますが、Radixに匹敵するアクセシビリティに関する公式の説明は見つかりませんでした。頼りにするコンポーネントについては、キーボードとスクリーンリーダーでの動作をテストしてください。

ライセンス。MIT(オープンソースのライブラリの場合。Flowbiteは別途、Proブロックやデザインファイルも販売しています)。

MUI (Material UI)

概要。自らの言葉では「GoogleのMaterial Designを実装したオープンソースのReactコンポーネントライブラリ」です。この比較の中で最も大きなセットで、データ表示、入力、ナビゲーション、レイアウト、フィードバックのコンポーネントを含みます。

スタイリング。CSS-in-JSです。既定ではEmotionを使います(@emotion/reactと@emotion/styledは@mui/material 9のピア依存関係です)。テーマはcreateThemeで設定し、個別のスタイルはsxプロパティで付けます。Tailwindと併用もできますが、自分でつながない限り、2つのテーマの仕組みはトークンを共有しません。

バージョンとライセンス。バージョン9(9.0.0は2026年4月に公開)。中核のライブラリはMITです。MUI X ProとPremiumにある高度なデータグリッド、日付、チャートの機能は商用ライセンスです。

アクセシビリティ。コンポーネントにはARIA属性とキーボード処理が備わっており、MUIのドキュメントの各コンポーネントのページで、アクセシビリティ上の動作が個別に説明されています。

注意点。Materialの見た目から離れるには相当なテーマ作業が必要で、CSS-in-JSはReact Server Componentsと組み合わせる際に追加の設定が要ります。

同じ6つのコンポーネントを各ライブラリで

以下では、同じボタン、ダイアログ、ドロップダウンメニュー、タブ、スイッチ、トーストを、各ライブラリの公式APIで1回ずつ、計6回書いています。どのコード例もこのページのために書いたオリジナルのコードで(各ライブラリのドキュメントからのコピーではありません)、各ライブラリの現在のメジャーバージョンを使い、違いのある部分だけに絞っています。実際にクリックできる版はRadix Primitivesのみで示しています。このサイトにインストールされているライブラリがRadixだからです。Radixにはスタイルがないので、私たち独自のTailwindクラスでスタイリングしています。ほかの5つのライブラリの動く版は示していないので、ここにそれらの見た目を模したものはありません。

ボタン

ほとんどのライブラリには、バリエーション付きのボタンがあります。RadixとHeadless UIは見た目を任せている一方、daisyUIはバリエーションをクラスで、ほかのライブラリはプロパティで表している点に注目してください。

ボタン: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>

ボタン: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} />;
}

ボタン: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>

ボタン: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>

ボタン: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>

ボタン: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>

ダイアログ(モーダル)

ダイアログには、アクセシビリティの作業が隠れています。フォーカスの閉じ込め、Escapeで閉じる、開いたボタンへのフォーカスの返却、ラベル付けです。ヘッドレスのライブラリとMUIはこれをJavaScriptで行います。daisyUIはネイティブの<dialog>要素に頼り、Escapeとフォーカスはブラウザーが処理します。

ダイアログ(モーダル):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>

ダイアログ(モーダル):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>

ダイアログ(モーダル):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>

ダイアログ(モーダル):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>

ダイアログ(モーダル):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>

ダイアログ(モーダル):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>

ドロップダウンメニュー

メニューには、矢印キーでの移動、先頭文字での検索、正しいロールが必要です。Radix、Headless UI、shadcn/ui、MUIはこれに対応しています。daisyUIの<details>版はJavaScriptなしで開閉しますが、矢印キーでの移動は加わりません。

ドロップダウンメニュー: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>

ドロップダウンメニュー: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>

ドロップダウンメニュー: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>

ドロップダウンメニュー: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>

ドロップダウンメニュー: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>

ドロップダウンメニュー: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>

タブ

正しいタブには、role="tablist"、role="tab"、aria-selectedと、矢印キーによるタブ間の移動が必要です。daisyUIとMUIではパネルとIDを自分でつなぎ込みますが、ほかのライブラリは自動でつないでくれます。

タブ: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>

タブ: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>

タブ: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>

タブ: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>

タブ: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>

タブ: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>

スイッチ(トグル)

スイッチは、role="switch"として読み上げられるオン・オフのコントロールです。daisyUIは本物のチェックボックスにスタイルを付けるので、ネイティブのフォームの動作が保たれます。ほかのライブラリは、スイッチのロールを持つボタンを描画します。

スイッチ(トグル):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>

スイッチ(トグル):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>

スイッチ(トグル):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>

スイッチ(トグル):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>

スイッチ(トグル):Flowbite React

import { ToggleSwitch } from "flowbite-react";

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

スイッチ(トグル):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"
/>

トースト(通知)

トーストは、ライブラリ間で最も違いが大きい部分です。状態で制御するコンポーネントを提供するものもあれば、どこからでも呼べる関数を提供するものもあり、Headless UIにはそもそもありません。

トースト(通知):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>

トースト(通知):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>

トースト(通知):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>

トースト(通知):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>
)}

トースト(通知):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>
)}

トースト(通知):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>}
/>

プロジェクトの種類別の選び方

プロジェクト向いているもの理由
Tailwindで作るマーケティングサイトやランディングページdaisyUI、または素のTailwindほぼ静的なページに必要なのはボタン、カード、メニューで、JavaScriptのコンポーネント層ではありません。daisyUIのクラスはReact以外のページでも使えます。
独自のブランドを持つSaaSプロダクトshadcn/ui、または自分でスタイリングしたRadix / Headless UIほかの製品の見た目を引き継がずにアクセシブルな動作が得られ、自分のデザインシステムに正確に合わせられます。
独自のデザインシステムやコンポーネントライブラリの構築Radix Primitives(またはBase UI)、Headless UIスタイルのないプリミティブは、その上に自分のスタイルを載せたい層そのものです。shadcn/ui自体がこのやり方の一例です。
社内ツール、管理パネル、ダッシュボードMUI、またはshadcn/ui見た目より早く公開することが大事な場合、MUIの幅広さ(表、MUI Xの日付選択、フォーム)が最も時間を節約します。すでにTailwindを使っているならshadcn/ui。
TailwindでのプロトタイプやハッカソンFlowbite ReactまたはdaisyUIごくわずかなスタイリング作業で、完成度の高い見た目のコンポーネントが手に入ります。プロトタイプが製品になるなら、見直す前提でいましょう。
すでにMaterial Design(またはAndroid風のUI)を使っているチームMUIMaterial Designをそのまま実装しているので、デザイナーと開発者が同じ言葉で話せます。
厳しいアクセシビリティ要件(公共部門、大企業)Radix Primitives、Headless UI、MUI、またはそれらの上に作ったshadcn/uiキーボードとフォーカスの動作が組み込まれ、文書化されています。どれを選んでも、キーボードとスクリーンリーダーでのテストは欠かせません。ライブラリだけでページがアクセシブルになることはありません。

選ぶ前に決めておくこと

これらのコンポーネントの土台となるフックについては、Reactフックのチートシートをご覧ください。コード例全体で使っているユーティリティクラスはTailwindチートシートで、どのボタンにも必要なホバー、フォーカス、無効の状態はCSSボタンの状態で解説しています。コンポーネントギャラリーでは、ReactとTailwindで作った既製のモーダル、ボタン、ナビゲーションも閲覧できます。

出典

2026年10月に確認:shadcn/uiのドキュメントと変更履歴(ui.shadcn.com)、shadcn-ui/uiのGitHubリポジトリ、アクセシビリティの概要を含むRadix Primitivesのドキュメント(radix-ui.com/primitives)、Headless UI(headlessui.com)、daisyUIのインストール、アップグレード、コンポーネントの各ページ(daisyui.com)、MUI Material UIのドキュメント(mui.com)、そしてバージョン、ライセンス、ピア依存関係については、shadcn、radix-ui、@headlessui/react、daisyui、flowbite-react、@mui/materialのnpmレジストリの掲載情報。

よくある質問

shadcn/uiはコンポーネントライブラリですか?

一般的な意味では違います。公式ドキュメント自身が、コンポーネントライブラリではなく、自分のライブラリを作るための方法だと説明しています。CLIがコンポーネントのソースをプロジェクトにコピーし、そのコードは自分で編集・保守します。操作できる部分はヘッドレスライブラリの上に作られており、2026年7月以降の新しいプロジェクトでは既定がBase UIで、Radixも引き続きサポートされています。

RadixとHeadless UIの違いは何ですか?

どちらもスタイルのない、アクセシブルなReactコンポーネントです。Radix Primitivesはトースト、ツールチップ、スライダー、ツールバーを含むより大きなセットで、React 16.8から19に対応しています。Headless UIはTailwind CSSチームによるもので、トーストやツールチップのない小さめのセットで、React 18または19が必要です。

daisyUIはReactで使えますか?

使えます。daisyUIはbtn、modal、tabsのようなクラス名を追加するTailwind CSSプラグインなので、ほかのTailwindクラスと同じようにJSXの中でそのクラスを使います。Reactコンポーネントは提供しないため、モーダルを開く、タブを切り替えるといった状態は自分のReactコードで扱います。

これらのライブラリは商用利用でも無料ですか?

はい。執筆時点(2026年10月)で、shadcn/ui、Radix Primitives、Headless UI、daisyUI、flowbite-react、そしてMUIの中核であるMaterial UIパッケージは、すべてMITライセンスです。MUI X ProとPremiumのコンポーネント、FlowbiteのProブロックは、別の商用製品です。

アクセシビリティに最も優れたReact UIライブラリはどれですか?

Radix Primitives、Headless UI、MUIは、キーボード操作とフォーカス管理をコンポーネントに組み込んでおり、shadcn/uiは土台のプリミティブ層からそれを引き継いでいます。daisyUIはネイティブのHTML要素に頼っているので、ARIAとキーボードの動作は自分で追加します。どれを使うにしても、キーボードとスクリーンリーダーでテストしましょう。

MUIはTailwind CSSと一緒に使えますか?

使えますが、スタイルの付け方が異なります。MUIはEmotionとJavaScriptのテーマを使い、Tailwindはユーティリティクラスを使います。両者は並べて使えますが、2つのテーマの間で色や余白の値を自分でそろえ続ける必要があります。すべてをTailwindで扱いたいチームは、たいていshadcn/uiかヘッドレスのライブラリを選びます。

ライブ:Radix Primitivesの{title}(スタイルなしのライブラリに、私たちのTailwindクラス)

プロフィールを編集

Tabを押すと、フォーカスは中に留まります。Escapeを押すか外側をクリックすると閉じます。

表示名

キャンセル

保存

閉じる

まだありません

オプション

名前を変更

複製

移動(無効)

隠しファイルを表示

矢印キーや文字入力での移動を試してください。最後に選んだ項目:{last}

設定

アカウント

支払い

チーム

アカウント設定のパネルです。タブでは左右の矢印キーを使ってください。

支払いのパネルです。

チームのパネルです。

メール通知:オン

メール通知:オフ

変更を保存

変更を保存しました

このデモで{n}回保存しました。

このデモで{n}回保存しました。

保存を取り消す

元に戻す

無料で始める · クレジットカード不要

関連: ガイド|Webマーケティングと集客を学ぶ

製品

ご利用いただける方

機能

リソース

会社情報

サイトマップを見る