Loading...
Loading...
Expo and React Native done right. Expo Router, EAS Build, native modules, platform-specific patterns, and navigation.
npx skill4agent add ofershap/expo-best-practices expo-best-practicesimport { 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.tsxapp/// Each screen manually defines its own layout// app/_layout.tsx
import { Stack } from "expo-router";
export default function RootLayout() {
return <Stack />;
}_layout.tsxexpo build:ios
expo build:androideas build --platform ios
eas build --platform androidexpo build# 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 }]],
};if (Platform.OS === "ios") {
return <IOSComponent />;
}
return <AndroidComponent />;MyComponent.ios.tsx
MyComponent.android.tsximport { Platform } from "react-native";
const styles = StyleSheet.create({
container: Platform.select({
ios: { paddingTop: 20 },
android: { paddingTop: 0 },
}),
});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} />;# Manual font linking via native projectsimport * as Font from "expo-font";
await Font.loadAsync({
CustomFont: require("./assets/fonts/CustomFont.ttf"),
});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);// Manual Firebase/APNs setup, native configurationimport * as Notifications from "expo-notifications";
const token = await Notifications.getExpoPushTokenAsync();# Editing Info.plist or AndroidManifest.xml directly{
"expo": {
"name": "MyApp",
"slug": "my-app",
"plugins": ["expo-router"]
}
}import codePush from "react-native-code-push";
export default codePush(MyApp);import * as Updates from "expo-updates";
await Updates.checkForUpdateAsync();router.push("/profile");import { router } from "expo-router";
router.push({ pathname: "/profile/[id]", params: { id: userId } });app/_layout.tsxComponent.ios.tsxComponent.android.tsxexpo config pluginsapp.jsonapp.config.tseas.jsonexpo buildios/android/