← ブログ

shadcn/ui が Base UI に切り替わりました。コードで実際に変わることはこれです

shadcn/ui のデフォルトが Radix から Base UI に変わりました。ぶつかることになる API の変更のすべて、-b radix という逃げ道、そして痛くない移行の順番。

Preet Suthar · · 5分で読めます

  • React
  • shadcn/ui
  • Base UI
  • Radix

2026 年 7 月 3 日、npx shadcn init は Radix ではなく Base UI を選ぶようになりました。見出しだけを読んだなら、おそらく二つのうちどちらかを考えているでしょう。Radix は死んだ、あるいは何も変わっていない。どちらも正しくありません。

私は Base UI 上に作られた shadcn/ui レジストリの HextaUI をメンテナンスしているので、今年の大半を両方のプリミティブ層の中で過ごしました。これは 7 月にあってほしかったガイドです。何が変わり、何が変わらなかったか、ぶつかることになる API の違いのすべて、そして移行すると決めた場合の順番です。

何が変わったか、一段落で

新しい shadcn/ui プロジェクトには Base UI のプリミティブが入ります。Radix も引き続き完全にサポートされます。shadcn チームによれば、Base UI にしか存在しないコンポーネントを除き、すべての更新と新しいコンポーネントは両方のライブラリ向けに出荷されます。既存のプロジェクトには影響ありません。新規プロジェクトで Radix を使いたければ、フラグを一つ渡します:

npx shadcn@latest init -b radix

init を非対話で呼び、Radix を前提にしている CI やスクリプトがあるなら、新しいチェックアウトに驚かされる前に、いまそのフラグを追加してください。

なぜデフォルトが変わったのか

理由は二つ挙げられました。Base UI が安定版 1.6.0 に達し、週間ダウンロードが 600 万を超えていたこと、そして shadcn/create で作られたプロジェクトがすでに Radix より Base UI をおよそ 2 対 1 で選んでいたことです。デフォルトは人々の選択に従いました。

Base UI は Radix、Material UI、Floating UI を作った人たちによるもので、その系譜がはっきり出ています。Combobox、Autocomplete、Number Field、Toast、Preview Card など、Radix が出さなかったプリミティブもカバーしています。2026 年 9 月時点のバージョンは 1.8.0 です。

既存のアプリを移行すべきか?

おそらく今四半期ではなく、一度にでもありません。

アプリが安定していて、足りないプリミティブで困っておらず、チームの誰にも具体的な不満がないなら、Radix に留まってください。依存しているものがなくなることはありません。

二つ目のライブラリに手を伸ばさずに Combobox や Number Field が必要な場合、どのみち Tailwind v4 向けにコンポーネントを書き直している最中の場合、あるいは新しい shadcn/ui コンポーネントが最初に対象とする層にいたい場合は、移行してください。

どちらにしても、決める前に次のセクションを読んでください。違いは見た目だけのものではありません。

実際にぶつかる API の違いのすべて

これらは失敗の仕方で分類しました。最初のグループはビルドを壊します。二つ目のグループは問題なくコンパイルが通り、挙動が静かに変わります。こちらのほうがたちが悪いです。

ビルドが壊れる変更

asChild は render になる。 誰もが最初に出会うのがこれです。

// Radix
<DialogTrigger asChild>
  <Button>Open</Button>
</DialogTrigger>

// Base UI
<DialogTrigger render={<Button />}>Open</DialogTrigger>

render prop は要素か関数を受け取ります。関数形式では状態が渡されるので、開いているときにアイコンが変わるトリガーのようなものに便利です。

アンカー付きのポップアップに Positioner が加わる。 Menu、Select、Popover、Tooltip は Portal > Content から Portal > Positioner > Popup になります。Dialog に Positioner はなく、Backdrop と Popup を使います。

Overlay は Backdrop になる。 リネームして次へ進みます。

Select の値は null になりうる。 制御された Select が持つのは string ではなく Value | null です。useState<string>("") は useState<string | null>(null) になり、値を文字列として読んでいたすべての箇所にガードが必要です。

Accordion と Toggle Group の値は常に配列になる。 single モードでも value="a" は value={["a"]} になります。Tabs は単一の値のままです。

onOpenChange に第二引数が付く。 Radix では event.preventDefault() を呼んで閉じるのを止めます。Base UI は (open, details) を渡し、details.cancel() を呼びます。details.reason フィールドで、Escape キーだったのか、外側の押下だったのか、それ以外かがわかるので、「未保存の変更があります」ダイアログで便利です。

開いた状態のセレクターが変わる。 data-[state=open]: のバリアントは data-open: になります。これは className の文字列や cva のバリアントの中に隠れているので、移行完了と言う前に data-[state= を grep してください。

静かな挙動の変更

Tabs はデフォルトで手動アクティベーションになる。 Radix では矢印キーでフォーカスが移動し、同時にタブがアクティブになります。Base UI では矢印キーでフォーカスが移動し、Enter か Space を押してアクティブにします。テストやユーザーが Radix の挙動を期待しているなら、リストに activateOnFocus を設定してください。

チェックボックスとラジオのメニュー項目はメニューを開いたままにする。 CheckboxItem と RadioItem では closeOnClick のデフォルトが false です。通常の項目は従来どおり閉じます。こちらのほうが良いデフォルトだとは言えますが、ユーザーが気づく変更です。

ナビゲーションメニューが速く開く。 Base UI のホバー delay のデフォルトは 50ms です。Radix の delayDuration のデフォルトは 200ms でした。速いメニューは気持ちいいのですが、誰かがヘッダーをマウスで横切ったときに三つのパネルが立て続けに開きます。調整してください。

痛くない移行の順番

両方のライブラリをインストールしたままにします。コミットごとにコンポーネントを一つ移します。その都度テストを実行します。順番はこうです:

  1. ポップアップのない末端のコンポーネント: Switch、Checkbox、Slider、Toggle、Progress。ほとんどはリネーム作業です。
  2. Tabs と Accordion。 配列の値を直し、activateOnFocus をどうするか決めます。
  3. Dialog と Alert Dialog。 話をややこしくする Positioner がないここで、Backdrop と Popup の構造と details.cancel() のパターンを覚えます。
  4. Popover と Tooltip。 Positioner を追加します。
  5. Select、Combobox、Dropdown Menu、Context Menu。 null になりうる値、closeOnClick、そしてポップアップツリー全体。これらは最後に、最も多くのテストとともにやります。

コンポーネントを Base UI 版に置き換えるのは CLI コマンド一つで、ソースは git にあるので、信用せずに diff をレビューしてください:

npx shadcn@latest add dialog --overwrite

それから git diff components/ui/dialog.tsx を実行し、残す前にすべての行を読んでください。

私ならやらないこと

ツリー全体に codemod をかけて祈る、ということはしません。ビルドを壊す変更は見つけやすいものです。静かな変更はそうではなく、設定ページでタブを切り替えるのにキー操作が二回必要になったことを codemod は教えてくれません。

プリミティブ層の切り替えとコンポーネントの再設計を同じ PR でやることもしません。shadcn/ui の要点はコードを自分が所有することです。所有するとは diff を読むことです。

移行を代わりにやってほしい場合

HextaUI は shadcn/ui プロジェクトに追加するレジストリです。すべてのコンポーネントがすでに Base UI で書かれていて、shadcn/ui の名前と props を維持し、同じテーマトークンを使うので、npx shadcn@latest add https://hextaui.com/r/dialog.json --overwrite で Base UI の Dialog が components/ui の他のファイルの隣に入ります。コンポーネントは MIT で無料です。HextaUI と shadcn/ui の比較ページに、何が違うのか、素の shadcn/ui のほうが良い選択になるのはどんなときかをまとめています。

よくある質問

Radix は非推奨ですか? いいえ。shadcn/ui はすべてのコンポーネントを両方のライブラリ向けに提供していて、チーム自身もまだ本番で Radix を使っています。

一つのプロジェクトで Radix と Base UI のコンポーネントを混ぜられますか? はい。移行中は両方の依存関係を抱えます。多くのプロジェクトは長い間混在したままになりますが、それで問題ありません。

テーマトークンを変える必要はありますか? いいえ。--background、--primary、--radius などは変わりません。プリミティブ層は Tailwind 側から独立しています。

Base UI は React Server Components に対応していますか? Base UI のプリミティブは Radix と同じくクライアントコンポーネントです。"use client" ディレクティブは shadcn/ui が置いた場所にそのまま残してください。

公式の変更履歴はどこにありますか? 2026 年 7 月のエントリは ui.shadcn.com/docs/changelog/2026-07-base-ui-default にあります。