expo-best-practices

Expo and React Native done right. Expo Router, EAS Build, native modules, platform-specific patterns, and navigation.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "expo-best-practices" with this command: npx skills add ofershap/expo-best-practices/ofershap-expo-best-practices-expo-best-practices

When to use

Use this skill when working with Expo or React Native code. It teaches current best practices and prevents common mistakes that AI agents make with outdated patterns, manual native setup, and deprecated APIs.

Critical Rules

1. Use Expo Router for navigation

Wrong (agents do this):

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>
  );
}

Correct:

app/
  _layout.tsx
  index.tsx
  settings.tsx

Use the app/ directory convention. Files become routes automatically. New Expo projects include Expo Router by default.

Why: Expo Router is built on React Navigation, provides file-based routing, typed routes, deep linking, and integrates with Expo CLI. Manual React Navigation setup is redundant and misses Expo Router features.

2. Use expo-router layouts for navigation structure

Wrong:

// Each screen manually defines its own layout

Correct:

// app/_layout.tsx
import { Stack } from "expo-router";
export default function RootLayout() {
  return <Stack />;
}

Why: _layout.tsx defines shared navigation structure, tabs, stacks, and nested layouts. It centralizes navigation configuration.

3. Use EAS Build for native builds

Wrong:

expo build:ios
expo build:android

Correct:

eas build --platform ios
eas build --platform android

Why: expo build is deprecated. EAS Build is the current cloud build service with better caching, credentials management, and native module support.

4. Use Expo config plugins for native configuration

Wrong:

# Manually editing
ios/MyApp/Info.plist
android/app/src/main/AndroidManifest.xml

Correct:

// 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 }]],
};

Why: Direct edits to native files are overwritten on prebuild. Config plugins modify native files at build time and survive prebuild.

5. Platform-specific code: use platform extensions or Platform.select

Wrong:

if (Platform.OS === "ios") {
  return <IOSComponent />;
}
return <AndroidComponent />;

Correct:

MyComponent.ios.tsx
MyComponent.android.tsx

Or:

import { Platform } from "react-native";
const styles = StyleSheet.create({
  container: Platform.select({
    ios: { paddingTop: 20 },
    android: { paddingTop: 0 },
  }),
});

Why: Platform file extensions let the bundler include only the correct code per platform. Platform.select keeps conditional logic in one file when appropriate.

6. Use expo-image instead of React Native Image

Wrong:

import { Image } from "react-native";
<Image source={{ uri: url }} style={styles.image} />;

Correct:

import { Image } from "expo-image";
<Image source={url} contentFit="cover" style={styles.image} />;

Why: expo-image adds caching, placeholders, blurhash, and better performance. React Native's Image lacks these features.

7. Use expo-font for custom fonts

Wrong:

# Manual font linking via native projects

Correct:

import * as Font from "expo-font";
await Font.loadAsync({
  CustomFont: require("./assets/fonts/CustomFont.ttf"),
});

Why: expo-font handles loading and avoids manual native configuration.

8. Use expo-secure-store for sensitive data

Wrong:

import AsyncStorage from "@react-native-async-storage/async-storage";
await AsyncStorage.setItem("authToken", token);

Correct:

import * as SecureStore from "expo-secure-store";
await SecureStore.setItemAsync("authToken", token);

Why: AsyncStorage is not encrypted. expo-secure-store uses the platform keychain/Keystore for tokens and secrets.

9. Use expo-notifications for push notifications

Wrong:

// Manual Firebase/APNs setup, native configuration

Correct:

import * as Notifications from "expo-notifications";
const token = await Notifications.getExpoPushTokenAsync();

Why: expo-notifications abstracts push setup across iOS and Android. Manual Firebase/APNs configuration is error-prone and platform-specific.

10. Use app.json or app.config.ts for configuration

Wrong:

# Editing Info.plist or AndroidManifest.xml directly

Correct:

{
  "expo": {
    "name": "MyApp",
    "slug": "my-app",
    "plugins": ["expo-router"]
  }
}

Why: Expo manages native config from app.json/app.config.ts. Direct edits are overwritten on prebuild.

11. Use expo-updates for OTA updates

Wrong:

import codePush from "react-native-code-push";
export default codePush(MyApp);

Correct:

import * as Updates from "expo-updates";
await Updates.checkForUpdateAsync();

Why: expo-updates integrates with EAS and Expo's update service. CodePush requires separate setup and is redundant in Expo projects.

12. Use TypeScript with typed navigation params

Wrong:

router.push("/profile");

Correct:

import { router } from "expo-router";
router.push({ pathname: "/profile/[id]", params: { id: userId } });

Define route params in your route files. Expo Router provides typed route helpers when used with TypeScript.

Why: Typed params prevent runtime errors and improve editor support.

Patterns

  • Create routes under app/ with file names that map to URLs
  • Use _layout.tsx for shared layout, tabs, and stacks
  • Put platform-specific components in Component.ios.tsx and Component.android.tsx
  • Use expo config plugins in app.json / app.config.ts for any native config change
  • Use eas.json for EAS Build profiles

Anti-Patterns

  • Do not set up React Navigation manually in Expo projects
  • Do not use expo build (use EAS Build)
  • Do not edit ios/ or android/ directly in managed workflow
  • Do not use AsyncStorage for auth tokens or secrets
  • Do not use React Native's Image when expo-image is available
  • Do not use CodePush in Expo projects (use expo-updates)

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

building-native-ui

Consult these resources as needed:

Repository Source
18.2K1.5Kexpo
General

native-data-fetching

You MUST use this skill for ANY networking work including API requests, data fetching, caching, or network debugging.

Repository Source
12K1.5Kexpo
General

upgrading-expo

- ./references/new-architecture.md -- SDK +53: New Architecture migration guide

Repository Source
11.9K1.5Kexpo
General

expo-deployment

No summary provided by upstream source.

Repository SourceNeeds Review
10.5K-expo