Expo UI(@expo/ui)とは|SwiftUI・Jetpack Composeをネイティブ描画するReact Nativeコンポーネント
Expo UI(パッケージ名 @expo/ui)は、React NativeのコードからiOSでは本物のSwiftUI、Androidでは本物のJetpack Composeを描画するネイティブUIコンポーネント群です。ボタンやスライダーを自前で描き直すreact-native-paperやNativeBaseのようなスタイリングライブラリとは性質が異なり、JSXで書いたコンポーネントがOS純正のウィジェットとしてそのまま表示されます。2026年5月21日にリリースされたExpo SDK 56で安定版(stable)となり、Expo Goに同梱され、create-expo-appの既定テンプレートにも組み込まれました。この記事では、@expo/uiの正体・インストール・主要コンポーネント・既存パッケージのドロップイン置き換え・react-native-paperとの違い・導入判断までを、Expo公式ドキュメントの一次情報にもとづいて解説します。
まとめ
- @expo/uiは、JSXからiOS=SwiftUI/Android=Jetpack ComposeのネイティブUIを描画するコンポーネント群。JSでUIを再現する従来のUIライブラリとは根本が違う。
- インストールは
npx expo install @expo/uiの1行。SDK 55でJetpack Compose(Android)対応、SDK 56で安定版となりExpo Goでもそのまま動く。 - importパスは3系統:クロスプラットフォームの
@expo/ui(universal)、iOS特化の@expo/ui/swift-ui、Android特化の@expo/ui/jetpack-compose。 - Button・Switch・Slider・Picker・BottomSheet・List など主要コンポーネントを提供。すべて
Hostでラップして使う。 - @react-native-picker/picker や @gorhom/bottom-sheet など8つの定番コミュニティパッケージをドロップインで置き換え可能。
- ネイティブ描画ゆえの制約もある。Web対応は限定的で、細かなデザイン統一を優先する案件では従来ライブラリのほうが向く場合がある。
以降で、@expo/uiの仕組みからコンポーネントの使い方、導入すべき場面までを順に見ていきます。
Expo UI(@expo/ui)とは何か|ネイティブウィジェットを描画するコンポーネント群
検索で「Expo UI」と調べると、Expoが提供する汎用のUIキットのように紹介されることがありますが、実体は違います。@expo/uiはExpo公式が開発するパッケージで、公式ドキュメントは「Jetpack ComposeとSwiftUIで完全にネイティブなインターフェースを構築できる、ネイティブの入力コンポーネント群」と定義しています。対応プラットフォームはiOS・Android・tvOS、そしてExpo Goです。
スタイル付きViewではなくOS純正ウィジェットを描画する
最大の特徴は描画方式です。一般的なReact Native UIライブラリは、View や Text にスタイルを当てて「それらしい見た目」を再現します。@expo/uiは違い、書いたコンポーネントがiOSでは実際のSwiftUIビュー、Androidでは実際のJetpack Composeのコンポーザブルへ変換されます。つまり画面に出るのはOSが自前のアプリで使っているのと同じ部品です。OSのバージョンが上がってデザイン言語が更新されれば、アプリ側のコードを変えなくても新しい見た目に追従します。
react-native-paper・NativeBaseとの決定的な違い
react-native-paperやNativeBaseは、Material DesignなどのデザインをJavaScript側で描き直すアプローチです。デザインの一貫性をアプリ全体で細かく制御できる反面、OSの純正コンポーネントとは別物で、見た目や挙動が「本物のiOS/Androidらしさ」から少しずれます。@expo/uiはOS純正の部品をそのまま出すため、ネイティブらしい操作感・アクセシビリティ・アニメーションを追加実装なしで得られます。逆に、両OSでピクセル単位に同じ見た目へ揃えたい要件では、プラットフォームごとに描画が変わる@expo/uiはむしろ扱いにくくなります。この違いが両者の使い分けの軸になります。
expo-system-uiとの混同に注意
名前が似た expo-system-ui は別物です。expo-system-uiはアプリのルートビューの背景色やシステムのライト/ダーク外観を制御するための小さなAPIで、UIコンポーネントを提供するものではありません。GSCの実クエリでも「expo-system-ui」と「@expo/ui」が混在して当記事に届いており、混同しやすい語です。UI部品を並べたいなら@expo/ui、画面全体の背景や外観設定を扱いたいならexpo-system-uiと覚えておくと迷いません。
Expo UIのインストールと対応バージョン
導入は非常に短く、Expoプロジェクトの直下で次の1行を実行します。バージョン整合はExpoが解決するため、手動でバージョンを指定する必要はありません。
npx expo install @expo/ui
Jetpack Compose(Android)対応はExpo SDK 55で追加され、iOS(SwiftUI)とAndroid(Jetpack Compose)双方のAPIが2026年5月21日リリースのSDK 56で安定版に到達しました。安定版に伴い@expo/uiはExpo Goに同梱されたため、開発ビルドを作らずExpo Goアプリだけで主要コンポーネントを試せます。create-expo-appの既定テンプレートにも含まれるので、新規プロジェクトでは初期状態から使えます。対応バージョンは更新が速いため、導入時はExpo SDK 56で刷新された開発体験と新機能の全体像と注目ポイントの記事や公式ドキュメントで最新状況を確認してください。
universal/swift-ui/jetpack-composeの3つのimportパス
@expo/uiのimportパスは役割で3系統に分かれます。まず1つのコードでiOS・Androidの両方に出したいときは、パッケージのルート @expo/ui からuniversalコンポーネントを読み込みます。iOS固有の表現に踏み込みたいときは @expo/ui/swift-ui、Android固有なら @expo/ui/jetpack-compose を使います。universalは内部でAndroidなら @expo/ui/jetpack-compose、iOSなら @expo/ui/swift-ui へ委譲し、Webではreact-domやreact-native-webのJS実装にフォールバックします。まずはuniversalで組み、プラットフォーム特有の作り込みが必要な箇所だけ個別パスに切り替えるのが素直な進め方です。
主要コンポーネントとHostラップの基本形
@expo/uiのコンポーネントはネイティブのビュー階層に載るため、必ず Host でラップして使うという約束があります。Hostがネイティブ描画の入り口になり、その中にレイアウトや部品を置きます。公式ドキュメントの最小例は次の形です。
import { Host, Column, Button, Text } from '@expo/ui';
export default function Example() {
return (
<Host style={{ flex: 1 }}>
<Column spacing={12} alignment="center">
<Text>Hello, world!</Text>
<Button label="Press me" onPress={() => alert('Pressed')} />
</Column>
</Host>
);
}
この Host の中に、以下のコントロール・レイアウト・提示系コンポーネントを組み合わせて画面を作ります。
入力・コントロール系(Button/Switch/Slider/Picker ほか)
フォームや操作に使う部品として、Button・Switch・Checkbox・Slider・TextInput・Picker が用意されています。これらはiOSではSwiftUIの、AndroidではJetpack Composeの純正コントロールとして表示されるため、スワイプやフォーカス移動、アクセシビリティの挙動もOS標準に一致します。「expo ui button」「expo ui list」のような部品単位の検索が多いのは、この純正コントロールを個別に使いたい需要が大きいためです。選択メニュー(menu)系のUIは Picker のバリアントや、後述する @react-native-menu/menu のドロップイン置き換えで実現します。
レイアウト・表示系(Row/Column/Text/List ほか)
配置と表示には Row・Column・Spacer・ScrollView といったレイアウトプリミティブと、Text・Icon の表示系、コレクション表示の List(ListItem)・FieldGroup があります。上のコード例のように Column に spacing や alignment を渡すだけで、ネイティブのレイアウトエンジンが縦並び・中央寄せを解決します。行方向に並べたいときは Row、余白の調整は Spacer を挟みます。設定画面のように項目をグルーピングしたい場合は FieldGroup が便利です。
提示・開閉系(BottomSheet/Collapsible)
画面の一部を出し入れする部品として BottomSheet と Collapsible があります。BottomSheetはiOS・Androidそれぞれの純正シート表現で表示されるため、コミュニティ製のボトムシート実装で悩みがちなドラッグ挙動やセーフエリア対応をOS任せにできます。開閉できる折りたたみ領域を作りたいときはCollapsibleを使います。装飾的なモーダルを大量に自作するより、まずこれらの標準部品で要件が満たせないかを検討すると実装が軽くなります。
既存コミュニティパッケージのドロップイン置き換え
@expo/uiは、これまでReact Nativeで定番だった外部パッケージのドロップイン置き換え(同等のAPIで差し替えられる代替)を提供します。既存アプリの依存を@expo/uiへ寄せることで、ネイティブ描画の恩恵を受けつつ依存パッケージ数を減らせます。公式が挙げる代表的な置き換え対象は次のとおりです。
| 置き換え対象パッケージ | 用途 |
|---|---|
| @react-native-picker/picker | 選択ピッカー |
| @react-native-menu/menu | ネイティブメニュー |
| @react-native-segmented-control/segmented-control | セグメントコントロール |
| @react-native-community/datetimepicker | 日時ピッカー |
| @react-native-community/slider | スライダー |
| @react-native-masked-view/masked-view | マスクビュー |
| react-native-pager-view | ページャー |
| @gorhom/bottom-sheet | ボトムシート |
置き換え先のimportは @expo/ui/community/○○ の形にまとまっています。たとえばピッカーは @expo/ui/community/picker、ボトムシートは @expo/ui/community/bottom-sheet です。多くの場合はimport文の差し替えだけで移行できますが、@expo/uiの内部がSwiftUI・Jetpack Compose(従来のUIKitやAndroid Viewsではない)で動く都合上、一部のpropが未対応だったり挙動が異なったりする点には注意が必要です。すべてを一度に移行せず、更新頻度が高い部品や不具合の出やすい部品から順に差し替え、propの差異を確認しながら進めるのが現実的です。
useNativeStateによるネイティブ状態連携とちらつき対策
@expo/uiには、ネイティブ側の状態とJavaScriptを直結させる useNativeState フックがあります。これはSwiftUIのObservableObjectやJetpack ComposeのMutableStateといったネイティブの状態プリミティブをJavaScriptから制御するもので、UIスレッド駆動のコントロールをちらつき(flicker)なしで動かすことを狙います。スライダーやスイッチのように連続的に値が変わる操作では、JavaScript側で状態を往復させると1フレーム遅れて表示がカクつくことがあります。useNativeStateを使うと状態更新がネイティブのUIスレッドで完結するため、指の動きに追従した滑らかな反応が得られます。フォームコントロールやネイティブ状態駆動のアニメーションを作る場面で効果が大きいフックです。
クロスプラットフォーム対応の仕組みと制限
@expo/uiはiOS・Android・tvOSに加え、universalコンポーネント経由でWebにも一応対応します。ただし「同じコードがどこでも同じように動く」わけではなく、プラットフォームごとに描画の実体が変わる点を理解しておく必要があります。
universalが各プラットフォームへ委譲する仕組み
universalコンポーネント(@expo/ui ルートからのimport)は、単一のAPIで書けますが内部では委譲先が分かれます。Androidでは @expo/ui/jetpack-compose、iOSでは @expo/ui/swift-ui の実装に委譲され、それぞれのOS純正ウィジェットとして表示されます。同じ Button でもiOSとAndroidで見た目・挙動が各OSの流儀に沿って変わるのはこの委譲によるものです。両OSで意図的に異なる純正表現を出したい場合は、universalのまま書くのが最短になります。
Web対応の実装と限界
Webではuniversalコンポーネントがreact-domやreact-native-webを使ったJS実装にフォールバックします。つまりWebに関しては「SwiftUI/Jetpack Composeをネイティブ描画する」という@expo/uiの核心的な利点は働かず、他のReact向けUIと同様のJS描画になります。Webを主戦場にする案件では、@expo/uiの魅力の大半がモバイルにあることを踏まえ、Web側は別のUI手段(たとえばTailwindベースのNativeWindなど)と併用する構成も検討に値します。モバイル(iOS/Android)を主、Webを従とする方針が現実的です。
Expo UIを使うべき場面・避けるべき場面
@expo/uiは万能ではありません。ネイティブ描画という設計思想がそのまま向き・不向きを決めます。判断の軸をはっきりさせておきます。
向いている場面は、iOS・Androidそれぞれの「純正らしさ」を重視したいアプリ、標準的な設定画面・フォーム・リストが中心のアプリ、そして@react-native-picker/pickerや@gorhom/bottom-sheetなど個別パッケージの保守に疲れているチームです。純正部品に寄せることで、OSアップデート時のデザイン追従やアクセシビリティ対応をExpo側に任せられます。Expo Goで即試せるため、プロトタイピングの速さも利点です。
一方で避けたほうがよい場面もはっきりしています。ブランドの世界観に合わせて全画面をピクセル単位で作り込み、iOSとAndroidで完全に同一の見た目へ揃えたい要件では、プラットフォームごとに描画が変わる@expo/uiは不利です。この場合はJSで一貫して描くreact-native-paperやNativeBase、あるいはNativeWindのようなユーティリティCSSのほうが制御しやすいでしょう。また主戦場がWebのプロダクトでは、前述のとおり核心的な利点が働かないため採用の優先度は下がります。「OSの純正体験を活かすか、独自デザインを貫くか」——この一点で選べば判断を誤りにくくなります。
よくある質問
@expo/uiは安定版ですか?無料で使えますか?
iOS(SwiftUI)とAndroid(Jetpack Compose)のAPIはExpo SDK 56で安定版になりました。Jetpack Compose対応はSDK 55で追加され、SDK 56ではExpo Goへの同梱と create-expo-app既定テンプレートへの組み込みも行われています。@expo/uiはExpoのエコシステムの一部としてオープンソースで提供され、パッケージ自体の利用に費用はかかりません。バージョンごとに対応状況が動くため、採用前に公式ドキュメントで現行SDKの状態を確認してください。
Expo Goだけで試せますか?
はい。SDK 56で@expo/uiがExpo Goに同梱されたため、開発ビルド(development build)を作らなくてもExpo Goアプリだけで主要コンポーネントを表示・操作できます。前提として、使用するプロジェクトのSDKに対応したバージョンのExpo Goが必要です(Expo Goの入手経路や対応SDKは更新が速いため、最新は公式の案内で確認してください)。npx expo install @expo/ui で導入し、対応版のExpo Goで読み込めばそのまま動きます。ただしプラットフォーム固有の高度な作り込みや一部ネイティブ依存を伴うケースでは、開発ビルドが必要になる場合があります。
expo-system-uiとExpo UI(@expo/ui)は同じものですか?
別のパッケージです。expo-system-ui はアプリのルートビュー背景やシステムのライト/ダーク外観を制御するAPIで、UIコンポーネントは提供しません。ボタンやスライダーなどの部品を使いたい場合は@expo/uiを、画面全体の背景色やアピアランス設定を扱いたい場合はexpo-system-uiを選びます。名前が似ているため検索時に混同しやすい点に注意してください。
react-native-paperから乗り換えるべきですか?
目的次第です。iOS・Androidの純正らしい操作感を重視し、標準的なフォームやリストが中心なら@expo/uiへの移行で得るものが大きいです。逆に、独自デザインを両OSで一貫させたい・既存のテーマ資産が厚いといった場合は、JSで描くreact-native-paperを継続するほうが制御しやすいことがあります。全面移行ではなく、更新頻度が高い部品や不具合の出やすい部品から段階的に置き換えて相性を見るのが安全です。
Webでも同じコンポーネントが使えますか?
universalコンポーネントはWebにも対応しますが、Web上ではreact-domやreact-native-webによるJS実装にフォールバックし、SwiftUI/Jetpack Composeのネイティブ描画は行われません。したがってWebでは@expo/ui最大の利点が働きません。モバイルを主軸に据え、Webは別のUI手段と併用する構成が現実的です。