building-native-ui について
building-native-ui は Expo と React Native の UI 開発ガイドで、ネイティブコントロール、スタイル、レイアウト、操作を扱います。参考資料やコード規約に加え、Expo Go とカスタムビルドの使い分けも説明します。
主な機能
- 01ネイティブコントロールとアニメーションの資料
- 02レイアウトとレスポンシブ設計の指針
- 03プラットフォームのセマンティックカラー利用
- 04スタイルとコーディング規約
- 05Expo Go とカスタムビルドの指針
活用シーン
- 01Expo ルートの UI 構築
- 02ネイティブコントロールとアニメーションの実装
- 03画面サイズに応じたレイアウト対応
- 04カスタムネイティブビルドの要否判断
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ expo/ skills --skill building-native-uiインストール後、Claude Code で/building-native-uiを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlreferences/animations.mdcontrols.mdgradients.mdicons.mdmedia.mdstorage.mdvisual-effects.mdwebgpu-three.md詳細
Expo UI ガイドライン
ルート、リンク、スタック、タブ、モーダル、シート、ヘッダーには、expo-router スキルを使用してください。
参考資料
必要に応じて、以下のリソースを参照してください。
references/
animations.md Reanimated: entering, exiting, layout, scroll-driven, gestures
controls.md Native iOS: Switch, Slider, SegmentedControl, DateTimePicker, Picker
gradients.md CSS gradients via experimental_backgroundImage (New Arch only)
icons.md SF Symbols via expo-image (sf: source), names, animations, weights
media.md Camera, audio, video, and file saving
storage.md SQLite, AsyncStorage, SecureStore
visual-effects.md Blur (expo-blur) and liquid glass (expo-glass-effect)
webgpu-three.md 3D graphics, games, GPU visualizations with WebGPU and Three.js
アプリの実行
重要:カスタムビルドを作成する前に、必ず Expo Go を試してください。
ほとんどの Expo アプリは、カスタムネイティブコードなしで Expo Go で動作します。npx expo run:ios または npx expo run:android を実行する前に、次の手順を行ってください。
- まず Expo Go を起動する:
npx expo startを実行し、Expo Go で QR コードをスキャンします - 機能が動作するか確認する:Expo Go でアプリを十分にテストします
- 必要な場合にのみカスタムビルドを作成する:詳細は以下を参照してください
カスタムビルドが必要な場合
以下を使用する場合に限り、npx expo run:ios/android または eas build が必要です。
- ローカル Expo モジュール(
modules/内のカスタムネイティブコード) - Apple ターゲット(
@bacons/apple-targetsを介したウィジェット、App Clip、拡張機能) - Expo Go に含まれていないサードパーティ製ネイティブモジュール
app.jsonでは表現できないカスタムネイティブ設定
Expo Go が使える場合
Expo Go は、すぐに使える幅広い機能に対応しています。
- すべての
expo-*パッケージ(カメラ、位置情報、通知など) - Expo Router のナビゲーション
- ほとんどの UI ライブラリ(reanimated、gesture handler など)
- プッシュ通知、ディープリンクなど
迷った場合は、まず Expo Go を試してください。 カスタムビルドを作成すると複雑さが増し、開発サイクルが遅くなるうえ、Xcode/Android Studio のセットアップが必要になります。
コードスタイル
- 文字列の閉じ忘れに注意してください。ネストされたバッククォートはエスケープし、引用符も必ず正しくエスケープしてください。
- インポート文は必ずファイルの先頭に記述してください。
- ファイル名には必ずケバブケースを使用してください。例:
comment-card.tsx - ファイル名に特殊文字を使用しないでください。
tsconfig.jsonでパスエイリアスを設定し、リファクタリングでは相対インポートよりエイリアスを優先してください。
ライブラリの選択
- React Native から削除された Picker、WebView、SafeAreaView、AsyncStorage などのモジュールは使用しないでください。
- レガシーの expo-permissions は使用しないでください。
expo-avではなくexpo-audioを使用してください。expo-avではなくexpo-videoを使用してください。- SF Symbols には
expo-symbolsや@expo/vector-iconsではなく、source="sf:name"を指定したexpo-imageを使用してください。 - react-native の SafeAreaView ではなく
react-native-safe-area-contextを使用してください。 Platform.OSではなくprocess.env.EXPO_OSを使用してください。React.useContextではなくReact.useを使用してください。- 組み込み要素の
imgではなく、expo-imageの Image コンポーネントを使用してください。 - 液体ガラスの背景には
expo-glass-effectを使用してください。 - ネイティブのセマンティックカラーには、生の
PlatformColorではなく、expo-routerのColorを使用してください(型安全で、ライト/ダークモードに自動適応します)。 - SDK 56 以降では、
@react-navigation/*から直接インポートしないでください。代わりにexpo-router/react-navigationを使用してください(@react-navigation/native、/core、/elements、/routersをカバーします)。
レスポンシブ対応
- ルートコンポーネントは必ずスクロールビューで囲み、レスポンシブにしてください。
- より適切なセーフエリアのインセットを得るには、
<SafeAreaView>の代わりに<ScrollView contentInsetAdjustmentBehavior="automatic" />を使用してください。 - FlatList と SectionList にも
contentInsetAdjustmentBehavior="automatic"を適用してください。 - Dimensions API ではなく、Flexbox を使用してください。
- 画面サイズの測定には、常に
Dimensions.get()よりuseWindowDimensionsを優先してください。
動作
- より快適な体験を提供するため、iOS では条件に応じて expo-haptics を使用してください。
- React Native の
<Switch />や@react-native-community/datetimepickerなど、触覚フィードバックが組み込まれたビューを使用してください。 - ルートがスタックに属する場合、最初の子要素はほとんどの場合、
contentInsetAdjustmentBehavior="automatic"を設定した ScrollView にしてください。 - ページに
ScrollViewを追加する場合、ほとんどの場合、ルートコンポーネント内の最初のコンポーネントにしてください。 - コピーできるデータを含むテキストには、
<Text selectable />プロパティを使用してください。 - 1.4M や 38k のように、大きな数値の表記を整えることを検討してください。
- WebView または Expo DOM コンポーネント内でない限り、'img' や 'div' などの組み込み要素は使用しないでください。
スタイリング
Apple Human Interface Guidelines に従ってください。
全般的なスタイルルール
- margin や padding のスタイルより、flex の gap を優先します
- 可能な場合は、margin より padding を優先します
- stack ヘッダー、タブ、または ScrollView/FlatList の
contentInsetAdjustmentBehavior="automatic"を使い、常にセーフエリアを考慮します - 上下両方のセーフエリアのインセットを必ず考慮します
- スタイルを再利用するほうが効率的な場合を除き、StyleSheet.create ではなくインラインスタイルを使います
- 状態変化には、必ず入場アニメーションと退場アニメーションを追加します
- カプセル形状を作成する場合を除き、角を丸くするときは
{ borderCurve: 'continuous' }を使います - ページ上のカスタムテキスト要素ではなく、必ずナビゲーションスタックのタイトルを使います
- ScrollView に padding を設定する場合は、ScrollView 自体ではなく
contentContainerStyleに padding と gap を指定します(クリッピングを軽減できます) - CSS と Tailwind はサポートされていません。インラインスタイルを使ってください
色
ネイティブのセマンティックカラーには、expo-router の Color API を使います。これは PlatformColor の型安全なラッパーで、iOS UIKit のカラーを Color.ios.*、Android Material 3 のカラーを Color.android.material.*(静的)または Color.android.dynamic.*(Android 12 以降でユーザーの壁紙に適応)から利用できます。これらはデバイス上で解決され、ライト/ダークモードとアクセシビリティ設定に自動的に適応するため、ライト/ダーク別の hex テーブルや colors.web.ts ファイルを個別に管理する必要はありません。
Color はプラットフォーム固有のため、各値を Platform.select でラップし、Web 用のデフォルトの hex 値を指定します。パレットは theme/colors.ts にまとめ、どこでも colors をインポートしてください:
// theme/colors.ts
import { Platform } from "react-native";
import { Color } from "expo-router";
export const colors = {
label: Platform.select({
ios: Color.ios.label,
android: Color.android.dynamic.onSurface,
default: "#000000",
})!,
secondaryLabel: Platform.select({
ios: Color.ios.secondaryLabel,
android: Color.android.dynamic.onSurfaceVariant,
default: "#3c3c43",
})!,
separator: Platform.select({
ios: Color.ios.separator,
android: Color.android.dynamic.outlineVariant,
default: "#c6c6c8",
})!,
systemBackground: Platform.select({
ios: Color.ios.systemBackground,
android: Color.android.dynamic.surface,
default: "#ffffff",
})!,
systemBlue: Platform.select({
ios: Color.ios.systemBlue,
android: Color.android.dynamic.primary,
default: "#007aff",
})!,
};
import { colors } from "@/theme/colors";
<View style={{ backgroundColor: colors.systemBackground }}>
<Text style={{ color: colors.label }}>Title</Text>
</View>;
- iOS では、システムテーマが変わるとこれらの色が自動的に再解決されます。Android では、これらの色を描画するコンポーネント内で
useColorScheme()を呼び出すと、テーマが切り替わったときに再レンダリングされます(React Compiler がコンポーネントをメモ化する場合に必要です)。 Color/PlatformColorの値を Reanimated のスタイルに渡さないでください。代わりに静的な色を使います(references/animations.mdを参照)。Platform.select({...})!はstring | OpaqueColorValueを返します。ほとんどの React Native のスタイルプロパティはColorValue(string | OpaqueColorValue)を受け付けるため、問題なく使えます。ただし、一部のサードパーティ製プロパティはstringのみを受け付けます(例:expo-imageのtintColor)。必要に応じてキャストしてください:colors.label as string。
テキストのスタイル
- 重要なデータやエラーメッセージを表示するすべての
<Text/>要素に、selectableプロパティを追加します - カウンターは、値を揃えるために
{ fontVariant: 'tabular-nums' }を使います
シャドウ
CSS の boxShadow スタイルプロパティを使います。従来の React Native の shadow または elevation スタイルは絶対に使わないでください。
<View style={{ boxShadow: "0 1px 2px rgba(0, 0, 0, 0.05)" }} />
'inset' シャドウはサポートされています。