Radixはもう古い?今すぐBase UIを使うべき理由

BBetter Stack
Computing/SoftwareInternet Technology

Transcript

00:00:00Radix UIを使うのをやめて、次は何に移行すべきか?実はBase UIです。もしあなたがshadcn/uiファンなら
00:00:06すでに切り替えるという選択肢があります。まだBase UIについて聞いたことがない方のために説明すると
00:00:10これはRadix、Floating UI、Material UIのオリジナル開発者たちによるヘッドレスUIライブラリです。
00:00:15コンポーネントの機能性とアクセシビリティを提供しつつ、デザインは自分たちで自由に構築できます。
00:00:20LLMがまだエッジケースやアクセシビリティへの対応に不十分な昨今、この点は特に重要です。しかし今言ったことは
00:00:24まさにRadixそのものでもあります。では、なぜ新しいライブラリが必要なのでしょうか?
00:00:30早速本題に入って、主な違いをお見せしましょう。
00:00:35まず手始めに、Base UIのドキュメントをさっと見てみましょう。左側に
00:00:44利用可能なコンポーネントが並んでいます。コンポーネントライブラリに必要なものはほぼすべて揃っており
00:00:48Radixにはないコンボボックスのような高度なものまで含まれています。
00:00:52ご覧の通り、すべてのコンポーネントに、CSS Modulesなどを使った
00:00:57スタイリングの好例が載っています。必要に応じて、Tailwindの例に切り替えることも可能です。
00:01:01ドキュメントは非常に優れていますが、今日はドキュメントを比較しに来たわけではありません。最初の大きな違いを見ていきましょう。
00:01:06おそらく皆さんが最も耳にするであろう違い、それは
00:01:10Base UIが活発にメンテナンスされているのに対し、Radixはいわば「ゾンビ状態」にあるということです。
00:01:15GitHubのコントリビューションチャートを見ると、Base UIが成長し続けている一方で、
00:01:20Radixはたまにコミットがある程度です。過去1ヶ月にクローズされたPRやIssueを見れば
00:01:25その差はさらに歴然としています。
00:01:29Base UIが58件のIssueを解決し154件のPRをマージしたのに対し、Radixはゼロです。
00:01:36Radixに何が起きたのかを要約すると、WorkOSがRadixの背後にいた会社を買収しましたが
00:01:42Radix自体にはあまり投資しなかったため、Radixチームの多くが去り、現在の状態に至っています。
00:01:47Radixの共同創設者でさえ「最後の手段としてしかRadixは使わない」と言っており、彼は今
00:01:53Base UIの開発に携わっています。アプリの依存関係が活発にメンテナンスされ、
00:01:58将来修正されないバグに悩まされないようにすることは、非常に重要です。
00:02:02さらに良いことに、Base UIはRadixにかなり似せて作られているので、移行はそれほど難しくないはずです。
00:02:08とはいえ、いくつかの改善も盛り込まれています。私のお気に入りの一つは
00:02:13Radixにある “asChild” プロップの扱いです。まだ見たことがない方のために
00:02:17ここにRadixのSelect(セレクトボックス)があります。もしセレクトのトリガーとして
00:02:22カスタムコンポーネントをレンダリングしたい場合、単にSelectTriggerで囲むだけだと
00:02:27ラッパーコンポーネントが生成され、その中に(購読ボタンのような)ボタンが配置されます。
00:02:31しかし、その購読ボタン自体をセレクトのトリガーとして機能させたい場合、
00:02:36Radixではトリガーに “asChild” プロップを追加するだけで済みます。これにより
00:02:41Radixに対し、SelectTriggerのすべてのプロップと機能を、子として設定したコンポーネントに
00:02:46マージするよう指示します。このクラス名がボタンとマージされ、
00:02:50この場合は上書きされているのがわかります。そのプロップを削除して保存すると
00:02:55ただのボタンになりますが、機能的にはセレクトのトリガーとして動作しています。
00:03:00これは便利な機能ですが、明示的ではないという不満もよく聞かれました。
00:03:04私自身、コードをざっと見ている時にこのプロップを見落とし、問題の原因を見逃すことがありました。
00:03:09そこでBase UIでのやり方を見てみると、全く同じ機能を得ることができます。
00:03:13こちらにもセレクトのトリガーとして機能するボタンがありますが、
00:03:18コードを見ると、Base UIは “asChild” の代わりに “render” プロップを使用しています。
00:03:24この “render” プロップで、レンダリングしたいコンポーネントを直接指定します。
00:03:29これは些細な変更ですが、何をしているのかがより明確になったと感じます。
00:03:34文字通りこのコンポーネントをレンダリングしているので、子コンポーネントが使われているかや
00:03:39“asChild” プロップがあるかを探す必要がありません。小さな変更ですが、個人的には素晴らしいと思います。
00:03:43そして、”render” プロップの力はこれだけではありません。
00:03:48スイッチを作成する場合、”render” プロップに関数を渡すことができます。これにより、
00:03:52構築中のコンポーネントのステートやプロップにアクセスできるようになります。
00:03:56この「SwitchThumb」の例では、どのコンポーネントにプロップを適用するかを選択でき、
00:04:01さらにカスタムのレンダリングロジックや、ステートに基づいた
00:04:05カスタムスタイリングを適用できます。
00:04:10SwitchThumbでは、checked(チェック済み)、dirty(変更あり)、disabled(無効)、filled(塗りつぶし)など、多くを選択できます。
00:04:14ここでは単にチェックされているかどうかを確認し、それに応じて
00:04:18異なるアイコンをレンダリングしています。自分のカスタムコンポーネントにこの “render” プロップを
00:04:22組み込めるフックまで用意されています。少し高度な話になりましたが、
00:04:27Base UIを使えば、どんなカスタムな要望も実現できることがわかるはずです。
00:04:31セレクトコンポーネントに戻りましょう。次の違いは、Base UIが一部のコンポーネントで
00:04:35データ駆動型のアプローチを許可している点です。このセレクトの例で見てみましょう。
00:04:40これはRadixのコードです。ラベルと値を持つ配列があり、
00:04:44実際に値をセレクト内に表示するには、配列をループ(map)して
00:04:49RadixのSelectItemをレンダリングし、値をこのように追加する必要があります。
00:04:54Base UIでのやり方に切り替えると、非常によく似ているのがわかります。
00:04:59ラベルと値の配列があり、それをループしてSelectItemをレンダリングしています。
00:05:03しかし、もう一箇所、この “SelectRoot” にもその配列を含めています。
00:05:08配列を渡すことで、レンダリング前にコンポーネントがデータを把握できるようになります。
00:05:13これは、特にサーバーサイドレンダリング(SSR)においてパフォーマンスの向上に繋がります。
00:05:17ただ、一つ改善の余地があると思うのは、現時点では
00:05:22“items” プロップとして配列を渡しつつ、さらに下でもその配列をループさせている点です。
00:05:26つまり二箇所で配列を使っています。そうではなく、別のヘッドレスライブラリである
00:05:32React Ariaのような方式が良いと思います。こちらでは、動物の配列を “items” として渡し、
00:05:36その子要素では関数を使っています。これにより親要素に渡されたすべてのアイテムを把握できるので、
00:05:41配列を二度使う必要がなく、親要素がデータプロバイダーとして機能します。
00:05:45この点はまだ改善できる部分だと思います。
00:05:50さて、セレクトコンポーネントに戻って、別の違いをお見せしましょう。
00:05:55これはセレクト固有の機能です。先ほどの “render” プロップやデータ駆動のアプローチは
00:05:59ほぼすべてのコンポーネントに共通していますが、これはセレクトだけのものです。
00:06:03それは、マルチセレクト(複数選択)ができることです。これはRadixに欠けていた痛恨の機能でした。
00:06:08Base UIでは、SelectRootに “multiple” プロップを追加するだけで
00:06:13trueかfalseかを指定でき、それだけでマルチセレクトになります。非常に簡単です。
00:06:17さらに一歩進んで、Base UIにはRadixになかったコンボボックスや
00:06:22オートコンプリートといったコンポーネントもあります。活発にメンテナンスされているため、
00:06:27ユーザーの要望に素早く応えられるのが大きな利点です。
00:06:33RadixとBase UIの違いをあと二つお見せしましょう。セレクトコンポーネントには
00:06:38少し飽きたので、チェックボックスコンポーネントに切り替えます。
00:06:41最初にお見せしたい違いはスタイリングについてです。
00:06:45Base UIには、非常にクールだと思えるもう一つのスタイリングの選択肢があります。
00:06:50今はTailwindを使っていますが、もちろん通常のCSSやCSS Modulesなど、
00:06:55従来のアプローチもすべて使えます。Tailwindを使っている場合、通常は
00:06:59ステートに応じたデータ属性(data-attributes)を使ってスタイリングします。
00:07:04例えば “data-checked” があれば背景色をプライマリカラーにするといった具合です。
00:07:08Tailwindではスタイリングのための文字列が非常に長くなり、時として問題になります。
00:07:13そこでBase UIが提供する選択肢の一つが、クラス名に関数を使用することです。
00:07:17これによりコンポーネントのステートにアクセスできます。このチェックボックスの例では、
00:07:22ステートが “checked” か “disabled” かに基づいてスタイルを適用しています。
00:07:26単純な条件分岐で記述しており、コードをぱっと見た時に、どこで
00:07:31チェック時や無効時のスタイルが定義されているかがわかりやすいと感じます。
00:07:35一行の長い文字列からデータ属性を探す必要がありません。また、
00:07:40バニラCSSなどを使っている場合、これはさらに役立つでしょう。
00:07:45私が気に入っている「Tailwind Variants」という別のライブラリとも非常に相性が良いです。
00:07:49このように、ステートをチェックボックス関数に渡すだけで済みます。
00:07:53Tailwind Variantでは基本スタイルに加えてバリアントを定義でき、
00:07:58「checkedがtrueならこのスタイル」「disabledならこれ」と記述できます。
00:08:02データ属性を使うよりずっと明確だと思いますが、これは好みや慣れの問題でもあります。
00:08:06Base UIがこうした選択肢をすべて提供してくれるのは素晴らしいことです。
00:08:11ちなみに、クラス名に関数を使うという手法は、私がReact Ariaで
00:08:14最初に惚れ込んだ機能でもあります。学習曲線が急なReact Ariaと、
00:08:19シンプルで使いやすいRadix。Base UIはその中間地点で両者の良いとこ取りをし、
00:08:23究極のヘッドレスライブラリを作り上げたように感じます。
00:08:28この動画で紹介したかった主な違いは以上ですが、まだまだ他にもたくさんあります。
00:08:33Base UIはReact Hook FormやTanStack Formを強力にサポートしており、
00:08:38アニメーション対応も容易です。さらにはインプットスクラブやダイアログのネスト、
00:08:42ホバーによるメニュー表示などの機能も備えています。妥当なリクエストがあれば、
00:08:47活発にメンテナンスされているGitHub上で対応してもらえるはずです。
00:08:52ただ、Radixを使っているアプリを今すぐBase UIに移行すべきだとは言いません。
00:08:57Radixが完全に壊れているわけではないからです。ですが、新規プロジェクトを始めるなら迷わずBase UIを使いますし、
00:09:02コンボボックスやオートコンプリートのような機能が必要なら、移行を検討する価値は大いにあります。
00:09:07Base UIについてどう思うか、コメント欄で教えてください。ついでにチャンネル登録もお願いします。
00:09:11それでは、また次の動画でお会いしましょう。

Key Takeaway

Radix UIのメンテナンス停滞を受け、より高機能で開発が活発なBase UIが、モダンなReact開発における新たなヘッドレスUIの標準になりつつあります。

Highlights

Base UIはRadix、Floating UI、Material UIの主要開発者たちによる次世代ヘッドレスUIライブラリである

Radix UIのメンテナンスが停滞(ゾンビ状態)しているのに対し、Base UIは非常に活発に開発が進んでいる

"asChild"プロップを廃止し、より明示的でステートアクセスが容易な"render"プロップを採用している

Radixにはなかったコンボボックス、オートコンプリート、マルチセレクト機能が標準で提供されている

データ駆動型アプローチ(itemsプロップ)の採用により、SSRにおけるパフォーマンス向上が期待できる

クラス名に関数を使用することで、データ属性に頼らない柔軟で可読性の高いスタイリングが可能である

Timeline

Base UIの概要とRadixからの移行背景

動画の冒頭では、Radix UIに代わる新しい選択肢としてBase UIが紹介されています。このライブラリはRadixやMaterial UIのオリジナル開発者によって制作されており、アクセシビリティとカスタマイズ性を両立させています。ドキュメントにはCSS ModulesやTailwindを用いた豊富なスタイリング例が掲載されており、導入のしやすさが強調されています。Radixには存在しないコンボボックスなどの高度なコンポーネントが既に含まれている点も大きな特徴です。LLMがまだアクセシビリティ対応に不十分な現状において、信頼できるライブラリの重要性が語られています。

メンテナンス状況の比較:Radixの「ゾンビ状態」

スピーカーはGitHubの統計データを用いて、RadixとBase UIのメンテナンス状況の決定的な差を指摘しています。Radixは買収後の投資不足によりコアメンバーが離脱し、PRやIssueの解決が停滞している「ゾンビ状態」にあると説明されています。対照的にBase UIは膨大な数のIssueを解決しており、Radixの共同創設者さえもBase UIの開発に参加しているという事実が明かされます。アプリの長期的な保守性を考慮すると、活発なコミュニティを持つライブラリを選ぶことが不可欠です。バグ修正が期待できない古いライブラリからの脱却が強く推奨されています。

asChildプロップからrenderプロップへの進化

Radixで一般的だった"asChild"プロップの使いにくさを解消する、Base UIの"render"プロップについて詳しく解説されています。従来の"asChild"は暗黙的でコードの可読性を下げる要因になっていましたが、"render"プロップはレンダリング対象を明示するため直感的です。さらに、"render"に関数を渡すことでコンポーネントの内部ステート(checkedやdisabledなど)に直接アクセスできる利点があります。これにより、チェック状態に応じて動的にアイコンを切り替えるといった複雑なロジックが簡潔に記述可能です。独自のフックを使用することで、カスタムコンポーネントにもこの柔軟性を組み込める点が非常に強力です。

データ駆動型アプローチと待望のマルチセレクト

Base UIが採用しているデータ駆動型のアプローチと、新機能であるマルチセレクトについて紹介されています。SelectRootに"items"プロップとして配列を渡すことで、レンダリング前にデータを把握でき、SSRのパフォーマンスが向上します。また、Radixユーザーが長年待ち望んでいたマルチセレクト機能が、"multiple"プロップを追加するだけで簡単に実現できるようになりました。React Ariaのような他のライブラリの長所を取り入れつつ、使いやすさを維持している点が評価されています。コンボボックスやオートコンプリートの追加など、ユーザーの要望に対するレスポンスの速さが際立っています。

関数によるスタイリングと結論

最後はクラス名に関数を使用する革新的なスタイリング手法と、移行に関する最終的なアドバイスが語られています。Tailwindを使用する際、長いデータ属性の文字列を書く代わりに、ステートに基づいた条件分岐をJavaScriptで直接記述できるため可読性が向上します。これはTailwind Variantsなどのライブラリとも相性が良く、開発者の好みに合わせた柔軟な実装が可能です。Base UIはReact Ariaの高度な機能とRadixのシンプルさを兼ね備えた「究極のヘッドレスライブラリ」であると結論づけられています。既存プロジェクトの即時移行は必須ではありませんが、新規開発においてはBase UIが最良の選択肢であると締めくくられています。

Community Posts

View all posts