expo-best-practices について
Expo または React Native のコードを扱う AI エージェント向けのスキルです。ナビゲーション、ネイティブ設定、プラットフォーム別コード、ストレージ、アップデートなどの開発パターンをまとめています。
主な機能
- 01Expo Router によるファイルベースのナビゲーション
- 02設定プラグインによるネイティブ設定
- 03EAS Build によるネイティブビルド
- 04プラットフォーム別コードのパターン
- 05画像、フォント、機密データ向け Expo パッケージ
- 06型付きナビゲーションパラメーター
活用シーン
- 01ルートと共通レイアウトの構成
- 02ファイルを直接編集せずにネイティブ設定
- 03機密データとプッシュ通知の処理
- 04現行 API を使った Expo アプリの保守
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ofershap/ expo-best-practices --skill expo-best-practicesインストール後、Claude Code で/expo-best-practicesを入力して読み込まれたか確認します。
詳細
使用する場面
Expo または React Native のコードを扱う場合に、このスキルを使用します。現在のベストプラクティスを学び、古いパターン、手動でのネイティブ設定、非推奨 API によって AI エージェントがよく犯す間違いを防ぎます。
重要なルール
1. ナビゲーションには Expo Router を使用する
誤り(エージェントがよく行う方法):
import { createStackNavigator } from "@react-navigation/stack";
const Stack = createStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
正しい方法:
app/
_layout.tsx
index.tsx
settings.tsx
app/ ディレクトリの規約を使用します。ファイルは自動的にルートになります。新しい Expo プロジェクトには、デフォルトで Expo Router が含まれています。
理由: Expo Router は React Navigation を基盤とし、ファイルベースのルーティング、型付きルート、ディープリンクを提供し、Expo CLI と統合されています。React Navigation を手動で設定するのは冗長であり、Expo Router の機能を活用できません。
2. ナビゲーション構造には expo-router のレイアウトを使用する
誤り:
// Each screen manually defines its own layout
正しい方法:
// app/_layout.tsx
import { Stack } from "expo-router";
export default function RootLayout() {
return <Stack />;
}
理由: _layout.tsx は、共通のナビゲーション構造、タブ、スタック、ネストされたレイアウトを定義します。ナビゲーション設定を一元化できます。
3. ネイティブビルドには EAS Build を使用する
誤り:
expo build:ios
expo build:android
正しい方法:
eas build --platform ios
eas build --platform android
理由: expo build は非推奨です。EAS Build は、より優れたキャッシュ、認証情報の管理、ネイティブモジュールのサポートを備えた、現在のクラウドビルドサービスです。
4. ネイティブ設定には Expo config plugins を使用する
誤り:
# Manually editing
ios/MyApp/Info.plist
android/app/src/main/AndroidManifest.xml
正しい方法:
// app.config.ts
import { ConfigPlugin } from "expo/config-plugins";
const withMyApiKey: ConfigPlugin<{ apiKey: string }> = (config, { apiKey }) => {
config = withInfoPlist(config, (c) => {
c.modResults["MY_API_KEY"] = apiKey;
return c;
});
return config;
};
export default {
plugins: [["withMyApiKey", { apiKey: process.env.API_KEY }]],
};
理由: ネイティブファイルを直接編集すると、prebuild 時に上書きされます。Config plugins はビルド時にネイティブファイルを変更するため、prebuild 後も変更が保持されます。
5. プラットフォーム固有のコードには、プラットフォーム拡張子または Platform.select を使用する
誤り:
if (Platform.OS === "ios") {
return <IOSComponent />;
}
return <AndroidComponent />;
正しい方法:
MyComponent.ios.tsx
MyComponent.android.tsx
または:
import { Platform } from "react-native";
const styles = StyleSheet.create({
container: Platform.select({
ios: { paddingTop: 20 },
android: { paddingTop: 0 },
}),
});
理由: プラットフォーム固有のファイル拡張子を使うと、バンドラーは各プラットフォームに適したコードのみを含めます。Platform.select を使うと、適切な場合に条件分岐を 1 つのファイルにまとめられます。
6. React Native Image の代わりに expo-image を使用する
誤り:
import { Image } from "react-native";
<Image source={{ uri: url }} style={styles.image} />;
正しい方法:
import { Image } from "expo-image";
<Image source={url} contentFit="cover" style={styles.image} />;
理由: expo-image はキャッシュ、プレースホルダー、blurhash、より優れたパフォーマンスを提供します。React Native の Image には、これらの機能がありません。
7. カスタムフォントには expo-font を使用する
誤り:
# Manual font linking via native projects
正しい方法:
import * as Font from "expo-font";
await Font.loadAsync({
CustomFont: require("./assets/fonts/CustomFont.ttf"),
});
理由: expo-font はフォントの読み込みを処理し、ネイティブ設定を手動で行う必要をなくします。
8. 機密データには expo-secure-store を使用する
誤り:
import AsyncStorage from "@react-native-async-storage/async-storage";
await AsyncStorage.setItem("authToken", token);
正しい方法:
import * as SecureStore from "expo-secure-store";
await SecureStore.setItemAsync("authToken", token);
理由: AsyncStorage は暗号化されていません。expo-secure-store は、トークンやシークレットの保存にプラットフォームのキーチェーン/Keystore を使用します。
9. プッシュ通知には expo-notifications を使用する
誤り:
// Manual Firebase/APNs setup, native configuration
正しい方法:
import * as Notifications from "expo-notifications";
const token = await Notifications.getExpoPushTokenAsync();
理由: expo-notifications は、iOS と Android のプッシュ通知設定を抽象化します。Firebase/APNs を手動で設定する方法は、エラーが発生しやすく、プラットフォームごとの対応が必要です。
10. 設定には app.json または app.config.ts を使用する
誤り:
# Editing Info.plist or AndroidManifest.xml directly
正しい方法:
{
"expo": {
"name": "MyApp",
"slug": "my-app",
"plugins": ["expo-router"]
}
}
理由: Expo は app.json/app.config.ts からネイティブ設定を管理します。直接編集すると、prebuild 時に上書きされます。
11. OTA アップデートには expo-updates を使用する
誤り:
import codePush from "react-native-code-push";
export default codePush(MyApp);
正しい方法:
import * as Updates from "expo-updates";
await Updates.checkForUpdateAsync();
理由: expo-updates は EAS および Expo のアップデートサービスと統合されています。CodePush には別途設定が必要で、Expo プロジェクトでは重複する機能です。
12. 型付きナビゲーションパラメーターには TypeScript を使用する
誤り:
router.push("/profile");
正しい方法:
import { router } from "expo-router";
router.push({ pathname: "/profile/[id]", params: { id: userId } });
ルートファイルでルートパラメーターを定義します。TypeScript と併用すると、Expo Router は型付きルートヘルパーを提供します。
理由: 型付きパラメーターは実行時エラーを防ぎ、エディターのサポートを向上させます。
パターン
- URL に対応するファイル名を付けて、
app/の下にルートを作成します - 共通のレイアウト、タブ、スタックには
_layout.tsxを使用します - プラットフォーム固有のコンポーネントは
Component.ios.tsxとComponent.android.tsxに配置します - ネイティブ設定を変更する場合は、
app.json/app.config.tsでexpo config pluginsを使用します - EAS Build のプロファイルには
eas.jsonを使用します
アンチパターン
- Expo プロジェクトで React Navigation を手動で設定しないでください
expo buildを使用しないでください(EAS Build を使用してください)- マネージドワークフローで
ios/やandroid/を直接編集しないでください - 認証トークンやシークレットに AsyncStorage を使用しないでください
- expo-image が利用できる場合は、React Native の Image を使用しないでください
- Expo プロジェクトで CodePush を使用しないでください(expo-updates を使用してください)