React

Radix UIとは?PrimitivesとThemesの違い・導入手順【radix-ui 1.6対応】

Radix UIとは?PrimitivesとThemesの違い・導入手順【radix-ui 1.6対応】

Radix UIは、React向けのUIコンポーネント群です。スタイルを持たずアクセシビリティの挙動だけを提供する「Radix Primitives」と、デザイン済みの「Radix Themes」に分かれます。2026年9月時点のnpm最新版は、Primitivesの統合パッケージ radix-ui が1.6.7(2026年7月24日公開)、Themesの @radix-ui/themes が3.3.0(2026年1月31日公開)です。この記事では、2つの違いと選び方、現行の導入手順、全30コンポーネント、asChild とDialogの落とし穴、shadcn/ui・Base UIとの関係を、公式ドキュメントとローカルでの描画結果をもとに説明します。

まとめ:Radix UIの要点と選ぶ条件

  • Radix UIはPrimitives(無スタイル)とThemes(スタイル付き)の2本立てです。ThemesはPrimitivesの上に作られています。
  • Primitivesは npm install radix-ui の1パッケージで入れるのが公式の推奨です。個別の @radix-ui/react-dialog なども引き続き公開されています。
  • 公式ドキュメントのコンポーネントは30件です。Form・One-Time Password Field・Password Toggle FieldはPreview扱いです。
  • Dialogの「Titleが無い」警告はreact-dialog 1.1.17(2026年6月15日)で削除されました。Titleを付け忘れても開発中に気づけません。
  • shadcn/uiは2026年7月2日に新規プロジェクトの既定をBase UIへ変えましたが、Radixの非推奨化はしていません。動いているRadix製アプリを移す理由にはなりません。

Radix UIの構成と開発元

Radix UIはもともとModulzが開発していました。WorkOSは2022年6月1日にModulzの買収を発表しており、現在のリポジトリのライセンス表記は「MIT License, Copyright © 2022-present WorkOS」です。商用プロジェクトでも無料で使えます。

「Radix UI」は製品群の総称で、npmでは次のパッケージに分かれます。

製品 npmパッケージ 最新版 週間DL(2026-09-20〜26)
Primitives(統合) radix-ui 1.6.7 約1,607万
Primitives(個別) @radix-ui/react-dialog ほか 1.1.23(Dialog) 約8,409万(Dialog)
Themes @radix-ui/themes 3.3.0 約104万

個別パッケージは、直接導入したアプリだけでなく統合パッケージの依存関係としてもインストールされるため、DL数を利用者数として比較できません。shadcn/uiのnew-yorkスタイルは、2026年2月に統合パッケージへ移行しています。ほかにカラーパレットのRadix Colorsとアイコン集のRadix Iconsもありますが、UI部品ではありません。

Radix PrimitivesとRadix Themesの違いと選び方

Primitivesは、キーボード操作・フォーカス管理・ARIA属性だけを受け持ち、見た目はすべて利用者が書きます。Themesは、Primitivesを部品として使い、色・余白・角丸をprops(accentColor、radius など)で切り替える完成品のライブラリです。@radix-ui/themes 3.3.0は依存関係に radix-ui を含んでいます。

観点 Primitives Themes
見た目 なし(CSSを自分で書く) あり(props で調整)
同梱CSS なし styles.css 約794KiB(gzip 約85KiB)
スタイル手段 Tailwind・CSS Modules 等なんでも props とテーマトークンが前提
向く場面 自社デザインシステムの土台 管理画面・社内ツール・試作

選び方ははっきりしています。デザイナーが作ったデザインシステムを実装するならPrimitivesです。Themesはトークンとpropsで閉じた設計なので、Figmaのデザインに1pxずつ合わせる用途では上書きが増えます。公式のスタイリングガイドも、Themesの見た目から大きく離れたい場合は「Radix Themesが合っているか考え直す」よう書いています。逆に、デザイナーがいない社内ツールならThemesで画面を早く揃えられます。

Radix Primitivesの導入手順

radix-uiと@radix-ui/react-*の違い

公式のIntroductionは、radix-ui パッケージを入れて必要なプリミティブをimportする方法を推奨しています。バージョンの食い違いや重複を防げるためです。radix-ui 1.6.7は55個の @radix-ui/* パッケージを依存関係として束ねたもので、個別パッケージが廃止されたわけではありません。

npm install radix-ui@latest

1.6.3からはプリミティブごとのサブパスも用意され、import * as Popover from "radix-ui/popover"; のように書けます。公式は、バンドラーによってはこちらのほうが不要なコードを削りやすいと説明しています。

Dialogの最小構成とスタイルの当て方

各プリミティブは Root・Trigger・Content などの部品に分かれています。掲載TSXはTypeScript 5.9.3、React 19.3.0、radix-ui 1.6.7で、strictとskipLibCheckを有効にした型検査を通過しています。依存パッケージの型宣言は検査対象から除外しています。

"use client";
import { Dialog } from "radix-ui";

export function DeleteDialog({ onDelete }: { onDelete: () => void }) {
  return (
    <Dialog.Root>
      <Dialog.Trigger className="btn">削除</Dialog.Trigger>
      <Dialog.Portal>
        <Dialog.Overlay className="overlay" />
        <Dialog.Content className="content">
          <Dialog.Title>記事を削除しますか?</Dialog.Title>
          <Dialog.Description>この操作は取り消せません。</Dialog.Description>
          <Dialog.Close className="btn">キャンセル</Dialog.Close>
          <button className="btn danger" onClick={onDelete}>削除する</button>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  );
}

開閉などの状態は data-state="open" / "closed" 属性として出力されるので、CSSは属性セレクタで書きます。

.overlay { position: fixed; inset: 0; background: rgb(0 0 0 / .4); }
.content { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: min(90vw, 32rem); max-height: 85vh; overflow: auto; padding: 1.5rem; background: white; color: #111; }
.content[data-state="open"] { animation: fadeIn 150ms ease-out; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.btn[data-state="open"] { outline: 2px solid #3b82f6; }

Next.js App Routerでのuse client指定

DialogやTooltipなど部品パッケージの配布ファイルは、先頭に "use client" を持っています。それでも、onClick や useState を書くファイルは自分でClient Componentにする必要があるため、上の例のように先頭へ "use client" を付けます。なお radix-ui 1.6.6と1.6.7は「React Server Componentsとの互換性問題を起こした破壊的変更を戻した」リリースです。1.6.3〜1.6.5を固定しているプロジェクトは1.6.7へ上げてください。

Radix Themesの導入手順と注意点

Themesは、パッケージを入れ、CSSを読み込み、Theme でアプリ全体を包む3手順です。

npm install @radix-ui/themes
import "@radix-ui/themes/styles.css";
import { Theme } from "@radix-ui/themes";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="ja">
      <body>
        <Theme accentColor="crimson" grayColor="sand" radius="large">
          {children}
        </Theme>
      </body>
    </html>
  );
}

Theme は class="radix-themes" と data-accent-color・data-radius などの属性を持つdivとして描画され、各部品はこの属性を手がかりに色を決めます。そのため、自作のPortalで Theme の外に描画した要素には色が付きません。公式は、Portalの中身をもう一度 Theme で包む方法を案内しています。

もう1つの注意点はCSSの量です。styles.css は使う部品の数にかかわらず全部品分を含み、3.3.0では812,667バイトありました。自前のCSSとの優先順位を調整したい場合は、tokens.css・components.css・utilities.css に分けて読み込めます。Tailwind CSS v3の @tailwind base はボタンのリセットでThemesのボタン背景を消すことがあるため、公式はCSSレイヤーを分けるなどの回避策を挙げています。

公式サイトのThemesリリースノートは3.1.3で更新が止まっていて、3.2.0以降の変更はGitHubのCHANGELOGにしか載っていません。GitHubには未公開の3.4.0の項目もあるので、npmのlatestと突き合わせて読んでください。

Radix Primitivesのコンポーネント一覧

公式ドキュメントのComponents欄は30件、Utilities欄は5件です。

分類 コンポーネント
オーバーレイ Dialog・Alert Dialog・Popover・Hover Card・Tooltip・Toast
メニュー Dropdown Menu・Context Menu・Menubar・Navigation Menu
フォーム Checkbox・Radio Group・Switch・Select・Slider・Label・Toggle
フォーム(Preview) Form・One-Time Password Field・Password Toggle Field
構造・表示 Accordion・Collapsible・Tabs・Toggle Group・Toolbar
その他 Avatar・Aspect Ratio・Progress・Scroll Area・Separator
ユーティリティ Accessible Icon・Direction Provider・Portal・Slot・Visually Hidden

Preview扱いの3件のうち、One-Time Password FieldとPassword Toggle Fieldは、radix-ui 1.6.7では unstable_OneTimePasswordField・unstable_PasswordToggleField という名前でexportされます。名前に unstable_ が付いている間はAPIが変わる前提で使ってください。Combobox(入力しながら候補を選ぶ部品)と日付選択は一覧にありません。これらが必要なら、Comboboxを持つBase UIなど別のライブラリと組み合わせます。

asChildとSlotの仕様:実測で分かった落とし穴

asChild を付けると、Radixは既定の要素(Dialog.Triggerなら button)を描画せず、直下の子要素にpropsと挙動を渡します。内部で使われているのがSlotで、className は親と子の値が連結されます。radix-ui 1.6.7を react-dom/server で描画すると、次の4つの失敗が確認できました。

  • propsを展開しない自作コンポーネント:({children}) => <button>{children}</button> に asChild で渡すと、aria-haspopup・aria-expanded・data-state がすべて消え、クリックしてもDialogは開きません。エラーは出ません。
  • 子が2つ以上、または文字列だけ:Primitive.button failed to slot onto its children というエラーで描画が止まります。
  • asChildを付け忘れてbuttonを渡す:<button> の中に <button> が入る不正なHTMLになります。
  • aタグに渡す:<a href="#x" type="button"> のように、リンクにも type="button" が付きます。ページ遷移させたいならTriggerではなく普通のリンクにします。

自作コンポーネントを渡すときは、受け取ったpropsを要素へ展開します。React 19では ref もpropsとして届くので forwardRef は不要です。

import type { ComponentProps } from "react";
import { Tooltip } from "radix-ui";

function IconButton(props: ComponentProps<"button">) {
  const { className, ...rest } = props;
  return <button {...rest} className={["icon-btn", className].filter(Boolean).join(" ")} />;
}

export function SaveButton() {
  return (
    <Tooltip.Provider>
      <Tooltip.Root>
        <Tooltip.Trigger asChild>
          <IconButton aria-label="保存">保存</IconButton>
        </Tooltip.Trigger>
        <Tooltip.Portal>
          <Tooltip.Content sideOffset={4}>保存(Ctrl+S)</Tooltip.Content>
        </Tooltip.Portal>
      </Tooltip.Root>
    </Tooltip.Provider>
  );
}

Tooltipは Tooltip.Provider の外に置くと Tooltip must be used within TooltipProvider というエラーになります。アプリのルートに1つ置いておくのが簡単です。

Dialogのアクセシビリティ:Titleの警告が消えた後の確認方法

jsdomでDialogを開くと、Radixは次の処理を自動で行いました。

  • Content内の最初のフォーカス可能な要素へフォーカスを移す
  • Dialog以外の要素に aria-hidden="true" を付け、スクリーンリーダーから隠す
  • body に pointer-events: none を付け、背景をクリックできなくする
  • Titleに aria-labelledby、Descriptionに aria-describedby を自動で結び付ける

注意が要るのはTitleです。以前のreact-dialogは、Titleが無いと開発中に DialogContent requires a DialogTitle で始まるエラーをコンソールへ出していました。npmの配布ファイルを版ごとに比べると、この警告文は1.1.16まであり、1.1.17で消えています。react-dialogのCHANGELOGにも Removed dev-only warnings for dialogs when title and/or description is not rendered. とあります。現行版ではTitleが無くても何も表示されず、Dialogの aria-labelledby が欠けたまま動きます。

対策は2つです。見出しを画面に出したくないDialogでもTitleは削除せず、Visually Hiddenで包みます。Descriptionが要らない場合は、公式どおり aria-describedby={undefined} を渡します。

import { Dialog, VisuallyHidden } from "radix-ui";

<Dialog.Content aria-describedby={undefined}>
  <VisuallyHidden.Root asChild>
    <Dialog.Title>サイト内検索</Dialog.Title>
  </VisuallyHidden.Root>
  <label>検索語
    <input type="search" />
  </label>
</Dialog.Content>

警告に頼れなくなった以上、テストで getByRole("dialog", { name: "サイト内検索" }) のようにアクセシブルネームで要素を取得しておくと、Titleの付け忘れがテストの失敗として出ます。また、defaultOpen で最初から開いたDialogでも、Portalの中身はサーバー描画のHTMLに含まれませんでした。検索エンジンに読ませたい本文をDialogの中だけに置かないでください。

SwitchとCheckboxのフォーム送信値

SwitchとCheckboxは、ネイティブの input ではなく button として描画されます。<form> の中に置いたSwitchは次のHTMLになりました。

<button type="button" role="switch" aria-checked="true" data-state="checked" value="on">
  <span data-state="checked"></span>
</button>
<input type="checkbox" aria-hidden="true" tabindex="-1" name="n" checked="" value="on"
  style="position:absolute;pointer-events:none;opacity:0;margin:0;transform:translateX(-100%)"/>

フォーム送信に使われるのは、視覚的に隠されたcheckboxです。値を指定しなければ送信値は on で、オフのときはネイティブのcheckboxと同じく送信されません。name を付け忘れると値は送られないので、Server Actionsや FormData で受け取る場合は必ず name を渡します。Checkboxも同じ構造です。radix-ui 1.6.2ではフォームのリセット時にRadioGroup・Slider・Select・Switchの値が戻らない不具合が、1.6.3では「すべて選択」のような一括更新でclickイベントが親へ伝わる不具合が直っています。フォーム部品を使うなら1.6.3以降にしてください。

shadcn/ui・Base UIとの関係と2026年の選び方

shadcn/uiは2023年1月の公開時からRadixを土台にしてきました。2026年7月2日の変更履歴で、新規プロジェクトの既定をBase UIへ切り替えています。Base UIはRadix・Material UI・Floating UIの開発者が作っている無スタイルのライブラリで、@base-ui/react 1.8.0が2026年9月4日に出ています。

同じ変更履歴には Radix is not being deprecated. You do not need to migrate. とあり、Radix版も更新が続きます。新規でRadixを使うなら npx shadcn init -b radix を指定します。CIなどで shadcn init を非対話で実行しているなら、このフラグを付けないと次からBase UIで生成されます。

判断の基準は次のとおりです。

  • 既存のRadix製アプリ:現在の機能要件を満たしているなら、既定ライブラリの変更だけを理由に移行する必要はありません。asChild がBase UIでは render propになるなど合成APIが違い、置き換えは部品ごとの書き換えになります。
  • 新規で、Comboboxや複数選択のSelectが必要:Base UIを選びます。Radixにはこれらがありません。
  • 新規で、上の部品が要らない:どちらでも構いません。社内に asChild の書き方が定着しているならRadixのほうが教育コストが低く済みます。

両者のAPIの違いはBase UI(React)とは?Radix UI・MUIとの違いと使い方【v1.8対応】で、shadcn/uiの導入手順はshadcn/uiとは?読み方・導入手順とBase UI既定化の変更点をReact開発者向けに解説で詳しく扱っています。

MUI・Chakra UI・Headless UIとの比較

ライブラリ 最新版(npm) 見た目 主な用途
Radix Primitives radix-ui 1.6.7 なし デザインシステムの土台
Radix Themes 3.3.0 あり 素早い画面構築
Headless UI 2.2.10 なし 無スタイルのReact・Vue向けUI
Material UI 9.4.0 あり(Material Design) 業務アプリ全般
Chakra UI 3.37.0 あり propsでのスタイル指定

無スタイル同士で比べると、Radixのほうが部品数が多く、Headless UIに無いMenubar・Navigation Menu・Toast・Sliderを持っています。逆にComboboxはHeadless UIにあってRadixにありません。Headless UIの使い方はHeadless UIとは?React/Vueでの使い方・Tailwind連携・主要ライブラリ比較にまとめています。デザイン済みライブラリとの比較なら、Material Designに沿うかどうかが分かれ目で、沿うならMaterial UI、沿わないならRadix ThemesかChakra UIです。詳しくはMaterial UIとは:v9系の導入からGrid・テーマ設定までコード付きで解説とChakra UIとは?v3の使い方・インストール手順とv2移行の注意点【2026年9月】を参照してください。

よくある質問

Radix UIは無料で商用利用できますか?

できます。PrimitivesもThemesもMITライセンスです。

radix-uiと@radix-ui/react-dialogのどちらを入れればよいですか?

新規なら統合パッケージの radix-ui です。公式がこちらを推奨しており、プリミティブ間のバージョンの食い違いを防げます。既存プロジェクトの個別パッケージは、そのまま使い続けても問題ありません。

Radix UIは開発が終わったのですか?

終わっていません。radix-ui は2026年6月6日の1.5.0から7月24日の1.6.7まで続けて更新され、次の1.7.0のリリース候補もnpmに出ています。shadcn/uiも2026年7月の変更履歴で、Radixは非推奨にしないと明記しています。

Radix UIはReact 19に対応していますか?

対応しています。radix-ui 1.6.7と @radix-ui/themes 3.3.0のpeerDependenciesは、React 16.8から19までを許容しています。

Radix UIとTailwind CSSは併用できますか?

Primitivesなら問題なく併用できます。className を渡し、data-[state=open]: のような属性バリアントで状態ごとのスタイルを書きます。Themesと併用する場合は、Tailwindのbaseスタイルがボタンの背景を消すことがあるため、CSSレイヤーを分けてください。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.09.30 テックブログ OpenAI Dotsとは?常時稼働エージェントの権限設計と自社システム接続【2026年9月】
  2. 2026.03.10 コラム 年収の壁【2026年最新】178万円・136万円・130万円の一覧と手取りの分岐点
  3. 2026.04.20 テックブログ Chrome(Gemini)のSkillsとは?使い方・作成手順・利用条件と表示されない時の対処
  4. 2026.09.05 コラム 犯罪収益移転防止法の本人確認:2027年4月の対面IC読み取り義務化と改修要件
  5. 2026.09.27 コラム 法定調書合計表とは?令和8年分の書き方と提出義務、給与・支払データからの集計自動化

RELATED POSTS 関連記事

目次