Expo Platform Basics
10 examples to get you started with Expo Platform - 7 basic and 3 intermediate.
Search across all documentation pages
10 examples to get you started with Expo Platform - 7 basic and 3 intermediate.
Expo projects are scaffolded with create-expo-app, which pins the SDK, Metro config, and default TypeScript layout in one step.
npx create-expo-app@latest MyExpoApp --template blank-typescript
cd MyExpoAppConfirm the SDK pin in package.json before installing anything else:
{
"dependencies": {
"expo": "~57.0.4",
"react": "19.2.3",
"react-native": "0.86.0"
}
}Start the dev server and open the app on a device or simulator:
npx expo startPress i for the iOS Simulator, a for Android, or scan the QR code with Expo Go. Every runnable example below can replace App.tsx in the project you just created.
Tooling: These examples target Expo SDK 57 (
expo~57.0.4), React Native 0.86.0, and React 19.2.3.
Expo wraps React Native with tooling, a curated module set, and a managed native project - your UI code stays standard RN.
import { StatusBar } from "expo-status-bar";
import { StyleSheet, Text, View } from "react-native";
export default function App() {
return (
<View style={styles.container}>
<Text style={styles.title}>Expo + React Native</Text>
<Text style={styles.body}>
Same View/Text primitives - Expo supplies the dev server, config, and
native bootstrap.
</Text>
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: "center", padding: 24, gap: 8 },
title: { fontSize: 22, fontWeight: "700" },
body: { fontSize: 15, color: "#4b5563", lineHeight: 22 },
});expo-status-bar ship as Expo modules with native code precompiled for the SDKios/ and android/ generation via prebuild unless you eject or run bareRelated: create-expo-app Quickstart - templates and first-run verification | Expo Modules Core - how native modules bootstrap
A fresh Expo app is a small, predictable tree - learn these files before adding features.
MyExpoApp/
├── App.tsx # Root component (entry UI)
├── app.json # Static manifest (or app.config.ts)
├── package.json # SDK-pinned dependencies and scripts
├── tsconfig.json
├── assets/ # Icons, splash, images bundled at build time
└── node_modules/App.tsx is the default root - Expo Router projects swap this for an app/ directory insteadapp.json (or app.config.ts) is the single source of truth for app identity, icons, and native permissionsassets/ files are referenced by URI in config (icon, splash) and imported in componentsios/ or android/ folders yet in pure managed mode - they appear after npx expo prebuild or an EAS buildRelated: create-expo-app Quickstart - template choices and script reference
The expo package version is the contract every other Expo module must satisfy.
{
"name": "my-expo-app",
"main": "expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios"
},
"dependencies": {
"expo": "~57.0.4",
"react": "19.2.3",
"react-native": "0.86.0"
}
}expo ~57.0.4 locks the project to SDK 57 - mixing SDK 56 modules with SDK 57 expo causes native build failuresmain: "expo/AppEntry.js" wires Metro to Expo's bootstrap, which registers your root componentexpo start so LAN, tunnel, and dev-client flags stay consistent across the teamnpm install then npx expo doctor to catch version skew before your first buildRelated: Upgrading Expo SDK Versions - intentional bumps and
expo install --fix
These manifest keys define how the binary appears on device home screens and in app stores.
{
"expo": {
"name": "My Expo App",
"slug": "my-expo-app",
"version": "1.0.0",
"scheme": "myexpoapp",
"orientation": "portrait",
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash-icon.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"ios": {
"bundleIdentifier": "com.example.myexpoapp"
},
"android": {
"package": "com.example.myexpoapp"
}
}
}name is the user-visible label; slug is the URL-safe Expo project identifierscheme registers a custom URL scheme for deep links (myexpoapp://)bundleIdentifier / package must be unique per app - changing them after store release creates a new listingRelated: app.json & app.config.js - dynamic config and multi-environment manifests
expo start launches Metro, prints a QR code, and hot-reloads App.tsx on save.
# Default: LAN URL for devices on the same network
npx expo start
# Clear Metro cache after dependency or babel changes
npx expo start --clear
# Tunnel when LAN/firewall blocks device ↔ laptop (slower, but reliable)
npx expo start --tunnelimport { useState } from "react";
import { Pressable, StyleSheet, Text, View } from "react-native";
export default function App() {
const [count, setCount] = useState(0);
return (
<View style={styles.container}>
<Text style={styles.count}>{count}</Text>
<Pressable style={styles.button} onPress={() => setCount((c) => c + 1)}>
<Text style={styles.buttonText}>Increment - save to hot reload</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: "center", alignItems: "center", gap: 16 },
count: { fontSize: 48, fontWeight: "700" },
button: { backgroundColor: "#2563eb", paddingHorizontal: 20, paddingVertical: 12, borderRadius: 10 },
buttonText: { color: "#fff", fontWeight: "600" },
});--clear when imports resolve incorrectly after adding packages or changing babel.config.jsi / a to boot simulators without retyping flagsRelated: Expo Go vs Development Builds - which client opens from this command
Always add Expo modules through the CLI so semver ranges match the SDK compatibility table.
# Adds a module tested against SDK 57
npx expo install expo-camera expo-file-system
# After a manual package.json edit or SDK bump - reconcile all Expo deps
npx expo install --fixnpx expo install reads your pinned expo version and chooses compatible native module releasesnpm install expo-camera@latest can pull a module built for a newer SDK and break iOS/Android compile--fix is the first command to run after upgrading expo in package.jsonapp.json - installing is only step oneRelated: Upgrading Expo SDK Versions - doctor, changelog, and RN alignment
Expo Go is a sandbox app with prebuilt native modules - ideal for learning and UI work without compiling native code.
import { CameraView, useCameraPermissions } from "expo-camera";
import { Pressable, StyleSheet, Text, View } from "react-native";
export default function App() {
const [permission, requestPermission] = useCameraPermissions();
if (!permission?.granted) {
return (
<View style={styles.center}>
<Text style={styles.message}>Camera access is required.</Text>
<Pressable style={styles.button} onPress={requestPermission}>
<Text style={styles.buttonText}>Grant permission</Text>
</Pressable>
</View>
);
}
return <CameraView style={styles.camera} facing="back" />;
}
const styles = StyleSheet.create({
center: { flex: 1, justifyContent: "center", alignItems: "center", gap: 12, padding: 24 },
message: { fontSize: 16, textAlign: "center" },
button: { backgroundColor: "#2563eb", paddingHorizontal: 20, paddingVertical: 10, borderRadius: 8 },
buttonText: { color: "#fff", fontWeight: "600" },
camera: { flex: 1 },
});npx expo start with Expo Go - no Xcode or Android Studio required for this loopuseCameraPermissions) still execute real OS prompts inside the Expo Go shellRelated: Expo Go vs Development Builds - decision checklist and
expo-dev-client
Replace static app.json with TypeScript config when bundle IDs, names, or API hosts differ per environment.
import { ExpoConfig, ConfigContext } from "expo/config";
const APP_VARIANT = process.env.APP_VARIANT ?? "development";
export default ({ config }: ConfigContext): ExpoConfig => {
const isProd = APP_VARIANT === "production";
return {
...config,
name: isProd ? "My Expo App" : "My Expo App (Dev)",
slug: "my-expo-app",
ios: {
...config.ios,
bundleIdentifier: isProd
? "com.example.myexpoapp"
: "com.example.myexpoapp.dev",
},
android: {
...config.android,
package: isProd ? "com.example.myexpoapp" : "com.example.myexpoapp.dev",
},
extra: {
apiUrl: isProd ? "https://api.example.com" : "https://staging.example.com",
appVariant: APP_VARIANT,
},
};
};APP_VARIANT=development npx expo start
APP_VARIANT=production npx eas build --profile productionapp.config.ts is evaluated at build and prebuild time - not on every Metro reload...config so Expo CLI merges defaults (plugins, updates) instead of silently dropping themextra - read them with expo-constants in app codeRelated: app.json & app.config.js - config plugins and secrets-safe patterns | Environments & EAS Environment Variables - dev/staging/prod separation
Libraries with custom native code, private native modules, or config plugins not in Expo Go require a development build.
npx expo install expo-dev-client
npx expo run:ios
# or produce a installable dev client via EAS:
npx eas build --profile development --platform ios{
"expo": {
"plugins": [
[
"expo-build-properties",
{
"ios": { "deploymentTarget": "15.1" },
"android": { "minSdkVersion": 24 }
}
]
]
}
}expo-dev-client replaces Expo Go as the native shell while keeping Metro fast refreshnpx expo run:ios / run:android generates native projects locally via prebuild, then compiles a dev clientapp.config.ts and let EAS Build run prebuild in CI instead of hand-editing ios/ when possibleRelated: Expo Go vs Development Builds - feature matrix | Managed to Bare Migration - keeping upgrade paths when native folders exist
Associating the app with an Expo project unlocks cloud builds, submit, updates, and org-scoped secrets.
npx expo login
npx eas init{
"expo": {
"name": "My Expo App",
"slug": "my-expo-app",
"owner": "my-org",
"extra": {
"eas": {
"projectId": "00000000-0000-0000-0000-000000000000"
}
}
}
}import Constants from "expo-constants";
import { Text, View } from "react-native";
export default function App() {
const projectId = Constants.expoConfig?.extra?.eas?.projectId;
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>EAS project: {projectId ?? "not linked"}</Text>
</View>
);
}eas init creates or links a project under your Expo account and writes extra.eas.projectIdowner set to an organization slug routes builds and credentials to the team, not a personal accountslug aloneRelated: Expo Account, Projects & Permissions - roles and multi-app governance | Environments & EAS Environment Variables - per-environment secrets
Stack versions: This page was written for React 19.2.3, React Native 0.86.0, and Expo SDK 57 (
expo~57.0.4).
Reviewed by Chris St. John·Last updated Jul 16, 2026