Expo is an open-source platform for making universal native apps that run on Android, iOS, and the web from a single React and JavaScript/TypeScript codebase. It bundles a universal runtime, a large library of native modules (the Expo SDK), a file-based router, a command-line tool, and the build tooling that ties them together.
This guide is the complete, source-based reference for using Expo: installing it, running the CLI, configuring your project with app.json/app.config.js, routing with Expo Router, and consuming the Expo SDK. It is written for application developers. Architecture, the native Modules API, the config-plugin system, and contribution internals live in the companion Admin Guide.
Note
This guide documents the contents of the
expo/expomonorepo at SDK 56 (expo@56.0.5,@expo/cli@56.1.12). Expo Application Services (EAS) — the hosted build/submit/update cloud — is referenced where it integrates with the open-source tools, but EAS itself is a separate platform.
1. Quick Start
Create a new project and start the development server in under a minute. You need Node.js and a package manager (npm, yarn, pnpm, or bun).
npm
npx create-expo-app@latest my-app
cd my-app
npx expo start
pnpm
pnpm create expo-app my-app
cd my-app
pnpm expo start
yarn
yarn create expo-app my-app
cd my-app
yarn expo start
When the dev server starts, you will see output similar to:
Starting project at /Users/you/my-app
Starting Metro Bundler
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
█ ▄▄▄▄▄ █▀ ... █ ← scannable QR code
█ █ █ █▀▀▀█ ...
› Metro waiting on exp://192.168.1.20:8081
› Scan the QR code above with Expo Go (Android) or the Camera app (iOS)
› Press a │ open Android
› Press i │ open iOS simulator
› Press w │ open web
› Press r │ reload app
› Press m │ toggle menu
› Press ? │ show all commands
Scan the QR code with the Expo Go app (Android) or the system Camera (iOS) to open your app on a physical device, or press a/i/w to launch an emulator, simulator, or browser.
Tip
The newest project template enables Expo Router (file-based routing), typed routes, and the React Compiler out of the box. Your screens live in the
app/directory and become routes automatically.
2. Requirements
| Requirement | Detail |
|---|---|
| Node.js | An actively supported LTS release. Required for the CLI, Metro bundler, and tooling. |
| Package manager | npm, yarn, pnpm, or bun. The CLI auto-detects which one you use from your lockfile. |
| Git | Recommended for project scaffolding and version control. |
| Watchman | Recommended on macOS for fast file watching with Metro. |
| Xcode | Required to build/run the iOS app locally (npx expo run:ios). macOS only. |
| Android Studio + SDK | Required to build/run the Android app locally (npx expo run:android). |
| CocoaPods | Required for native iOS builds. Install pods with npx pod-install. |
Important
You do not need Xcode or Android Studio to start developing. You can run your app on a physical device through Expo Go or a development build and iterate entirely in JavaScript. Native toolchains are only needed when you build the native app yourself (
run:ios/run:androidorprebuild), and EAS Build can perform those builds in the cloud instead.
Run targets at a glance
| Target | How your app runs | When to use |
|---|---|---|
| Expo Go | A prebuilt sandbox app from the store that loads your JS bundle. | Quick prototyping with only SDK-provided native modules. |
| Development build | Your own debug build (via expo-dev-client) that includes any native code. |
Real projects with custom native modules or third-party native libraries. |
| Web | Metro bundles your app for the browser (react-native-web). |
Universal apps and static sites. |
3. Installation
3.1 Create a new project
npx create-expo-app@latest my-app
The scaffolder writes a project that includes the expo package, a starter app/ directory, and an app.json. A minimal generated app.json looks like this (from the default template):
{
"expo": {
"name": "HelloWorld",
"slug": "expo-template-default",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"scheme": "helloworld",
"userInterfaceStyle": "automatic",
"ios": { "icon": "./assets/expo.icon" },
"android": {
"adaptiveIcon": {
"backgroundColor": "#E6F4FE",
"foregroundImage": "./assets/images/android-icon-foreground.png",
"backgroundImage": "./assets/images/android-icon-background.png",
"monochromeImage": "./assets/images/android-icon-monochrome.png"
},
"predictiveBackGestureEnabled": false
},
"web": { "output": "static", "favicon": "./assets/images/favicon.png" },
"plugins": [
"expo-router",
["expo-splash-screen", { "backgroundColor": "#208AEF", "android": { "image": "./assets/images/splash-icon.png", "imageWidth": 76 } }]
],
"experiments": { "typedRoutes": true, "reactCompiler": true }
}
}
Several official templates are available. Pass one with --template:
| Template | Contents |
|---|---|
default |
Expo Router + TypeScript + tabs (the recommended starting point). |
blank |
Minimal single-screen JavaScript app. |
blank-typescript |
Minimal single-screen TypeScript app. |
tabs |
Expo Router with a tab layout. |
bare-minimum |
A bare project with native ios/ and android/ directories already generated. |
3.2 Add Expo to an existing React Native app
You can adopt the Expo SDK and Modules API in a plain React Native project:
npx install-expo-modules@latest
This installs expo and configures native autolinking so that Expo modules link into your existing ios/ and android/ projects.
Note
Once the
expopackage is installed and configured, you can add any Expo SDK package (for examplenpx expo install expo-camera) and it will autolink during your next native build.
3.3 Install SDK packages
Always add native packages with npx expo install rather than your raw package manager. expo install picks the version of each package that is compatible with your installed SDK:
npx expo install expo-camera expo-location expo-sqlite
4. The Expo CLI
The local CLI is invoked as npx expo <command> (the binary is provided by the @expo/cli package, re-exported through expo). Every command supports -h/--help. All commands accept an optional project directory; the default is the current working directory.
| Command | Purpose |
|---|---|
expo start |
Start a local dev server for the app. |
expo run <android|ios> |
Build and run the native app locally. |
expo prebuild |
Generate native iOS and Android project files (Continuous Native Generation). |
expo install |
Install a module or other package at an SDK-compatible version. |
expo export |
Export the app's static JS/assets (and web HTML) for hosting. |
expo serve |
Host an exported production server build locally. |
expo customize |
Generate static project files (e.g. metro.config.js, babel.config.js, index.js, +html.tsx). |
expo config |
Print the evaluated project config. |
expo lint |
Lint the project with ESLint. |
expo login / logout / register / whoami |
Manage your Expo account session for the CLI. |
Caution
Publishing and app-store distribution are handled by EAS (
eas build,eas submit,eas update) from the separateeas-clipackage — not by the localexpoCLI. Olderexpo build/expo publishcommands have been removed in favor of EAS.
4.1 expo start
Start the Metro dev server and serve your app to connected devices, simulators, or the browser.
npx expo start
| Option | Description |
|---|---|
-a, --android |
Open on a connected Android device or emulator. |
-i, --ios |
Open in an iOS simulator. |
-w, --web |
Open in a web browser. |
-d, --dev-client |
Launch in a custom native app (development build). |
-g, --go |
Launch in Expo Go. |
-c, --clear |
Clear the Metro bundler cache before starting. |
--max-workers <number> |
Maximum number of tasks Metro may spawn. |
--no-dev |
Bundle in production mode. |
--minify |
Minify JavaScript. |
-m, --host <type> |
Dev server hosting type: lan (default), tunnel, or localhost. |
--tunnel |
Shorthand for --host tunnel (route through ngrok; works across any network). |
--lan |
Shorthand for --host lan (local network). |
--localhost |
Shorthand for --host localhost. |
--offline |
Skip network requests and use anonymous manifest signatures. |
--https |
Start the dev server over HTTPS. (Deprecated in favor of --tunnel.) |
--scheme <scheme> |
Custom URI protocol to use when launching the app. |
-p, --port <number> |
Port for the dev server (default 8081; does not apply to web or tunnel). |
--private-key-path <path> |
Path to the private key used for code signing. |
# Open straight into the iOS simulator, clearing the cache
npx expo start --ios --clear
# Develop over a tunnel so a device on cellular can connect
npx expo start --tunnel
Tip
Use
--dev-clientwhen your project contains custom native modules. Use--goto force Expo Go even ifexpo-dev-clientis installed.
4.2 expo run:android / expo run:ios
Compile the native app and run it on a device, emulator, or simulator. This requires the native toolchains (Android Studio / Xcode) and runs prebuild automatically if the native directories don't exist yet.
npx expo run:android
npx expo run:ios
Both subcommands are permissive: any flags after the platform are forwarded to the underlying native build (for example --device, --variant, --configuration, --scheme). Run npx expo run:ios --help or npx expo run:android --help for the platform-specific options.
4.3 expo prebuild
Generate the native ios/ and android/ project files from your app.json and config plugins. This is Continuous Native Generation (CNG): the native projects are treated as build artifacts you can regenerate at any time.
npx expo prebuild
| Option | Description |
|---|---|
--no-install |
Skip installing npm packages and CocoaPods. |
--clean |
Delete existing native folders and regenerate them from scratch. |
-p, --platform <platform> |
Limit generation to android or ios. |
--template <template> |
Use a custom prebuild template. |
--npm |
Use npm to install dependencies (default when package-lock.json exists). |
--yarn |
Use Yarn (default when yarn.lock exists). |
--pnpm |
Use pnpm (default when pnpm-lock.yaml exists). |
--bun |
Use bun (default when bun.lock/bun.lockb exists). |
--skip-dependency-update <names> |
Skip updating the listed native dependencies. |
Warning
When you use CNG, treat
ios/andandroid/as generated output. Re-runningprebuild --cleandiscards manual edits to native files. To customize native code reproducibly, write a config plugin instead (see the Admin Guide).
4.4 expo export
Bundle the app's JavaScript and assets for hosting — used for EAS Update payloads and for exporting a static web site.
npx expo export
| Option | Description |
|---|---|
--output-dir <dir> |
Output directory for static files (default dist). |
--dev |
Configure static files for local development over a non-HTTPS server. |
--no-minify |
Do not minify source. |
--no-bytecode |
Do not generate Hermes bytecode. |
--max-workers <number> |
Maximum bundler worker tasks. |
--dump-assetmap |
Emit an asset map for further processing. |
--no-ssg, --api-only |
Skip static HTML export and export only API routes (web). |
-p, --platform <platform> |
android, ios, web, or all (default all). |
-s, --source-maps [mode] |
Emit JS source maps: true, false, inline, or external (default false). |
-c, --clear, --reset-cache |
Clear the bundler cache. |
# Export a static website to ./dist
npx expo export --platform web
4.5 expo serve
Host a production server build (from expo export with web.output: "server") locally to test it.
npx expo serve
| Option | Description |
|---|---|
--port <number> |
Port to host the server on. |
4.6 expo install
Add a package at a version compatible with your installed SDK, or check/fix the versions of already-installed packages.
npx expo install expo-camera
| Option | Description |
|---|---|
--check |
Check which installed packages need to be updated. |
--fix |
Automatically update any invalid package versions. |
--dev |
Save the dependencies as devDependencies. |
--npm / --yarn / --bun / --pnpm |
Choose the install command (auto-detected from your lockfile by default). |
--json |
Output dependency information as JSON (with --check). |
# Verify every installed package matches your SDK, then fix mismatches
npx expo install --check
npx expo install --fix
Tip
Pass extra flags through to the underlying package manager after
--. For example:npx expo install expo-camera -- --save-exact.
4.7 expo customize
Eject specific static project files so you can edit them — for example metro.config.js, babel.config.js, index.js, public/, or +html.tsx.
npx expo customize metro.config.js
4.8 expo config
Print your evaluated project configuration. Useful for debugging app.config.js, plugin output, and environment-dependent values.
npx expo config
| Option | Description |
|---|---|
--full |
Include all project config data. |
--json |
Output in JSON format. |
-t, --type <type> |
public, prebuild, or introspect. introspect shows the config after all plugins have run. |
# See exactly what your config plugins produce before prebuild
npx expo config --type introspect
4.9 expo lint
Lint files in your src/, app/, and components/ directories with ESLint, setting up an Expo ESLint config on first run if needed.
npx expo lint
4.10 Account commands
| Command | Description |
|---|---|
npx expo login |
Log in to an Expo account. |
npx expo logout |
Log out of the current account. |
npx expo register |
Sign up for a new Expo account. |
npx expo whoami |
Show the currently authenticated username. |
Note
For non-interactive/CI use, set the
EXPO_TOKENenvironment variable to a personal access token instead of runningexpo login. See the Environment Variables reference.
5. Project Configuration (app.json / app.config.js)
Your project is configured through the expo key of app.json, or programmatically via app.config.js/app.config.ts (which can read environment variables and return the config object). The schema below is the canonical ExpoConfig interface.
Important
Use
app.config.jswhen your config depends on environment variables or build profiles. It exports a function or object, e.g.export default ({ config }) => ({ ...config, extra: { apiUrl: process.env.API_URL } }). Values placed inextraare readable at runtime viaConstants.expoConfig.extra.
5.1 Top-level fields
| Key | Type | Description |
|---|---|---|
name |
string |
App display name, shown on the home screen and in Expo Go. |
slug |
string |
URL-friendly project name, unique within your account. |
version |
string |
Your app's user-facing version (maps to iOS CFBundleShortVersionString / Android versionName). |
owner |
string |
The Expo account that owns the project (useful for teams). |
sdkVersion |
string |
The Expo SDK version to run on; should match package.json. |
runtimeVersion |
string | { policy } |
Compatibility key between native code and OTA updates. Policies: nativeVersion, sdkVersion, appVersion, fingerprint. |
platforms |
('android'|'ios'|'web')[] |
Platforms your project supports. Defaults to ["ios","android"] (plus web if react-dom is installed). |
orientation |
'default'|'portrait'|'landscape' |
Locks screen orientation. Defaults to no lock. |
userInterfaceStyle |
'light'|'dark'|'automatic' |
Force light/dark appearance or adapt to system. Requires expo-system-ui on Android. |
backgroundColor |
string |
Root view background color. Requires expo-system-ui on iOS. |
primaryColor |
string |
Android multitasker accent color. |
icon |
string |
Path or URL to a 1024×1024 PNG app icon. |
scheme |
string | string[] |
Deep-link URL scheme(s), e.g. myapp://. Build-time only (no effect in Expo Go). |
extra |
object |
Arbitrary values exposed at runtime via Constants.expoConfig.extra. |
plugins |
(string | [string, any])[] |
Config plugins to extend native projects during prebuild. |
locales |
object |
Per-locale strings for system dialogs and permission prompts. |
githubUrl |
string |
Public repository URL, linked from your Expo project page. |
experiments |
object |
Opt-in experimental features (see below). |
updates |
object |
expo-updates (OTA) configuration (see below). |
ios |
object |
iOS-specific configuration (see §5.2). |
android |
object |
Android-specific configuration (see §5.3). |
web |
object |
Web-specific configuration (see §5.4). |
updates (OTA configuration)
| Key | Description |
|---|---|
enabled |
Whether the updates system runs (default true). |
checkAutomatically |
When to check for updates: ON_LOAD (default), ON_ERROR_RECOVERY, WIFI_ONLY, NEVER. |
fallbackToCacheTimeout |
Milliseconds to wait for a new update at launch before using the cached one (0–300000). |
url |
URL from which update manifests are fetched. |
useEmbeddedUpdate |
Whether to load the embedded update (default true). |
codeSigningCertificate / codeSigningMetadata |
PEM certificate + metadata for verifying signed updates. |
requestHeaders |
Extra HTTP headers sent when fetching manifests/assets. |
assetPatternsToBeBundled |
Glob patterns selecting which asset files are bundled into updates. |
experiments (opt-in features)
| Key | Description |
|---|---|
typedRoutes |
Statically typed links in Expo Router (requires TypeScript). |
tsconfigPaths |
Support compilerOptions.paths/baseUrl import aliases in Metro. |
reactCompiler |
Enable the React Compiler. |
reactCanary |
Use a vendored canary build of React. |
reactServerComponentRoutes |
React Server Components by default in Expo Router. |
reactServerFunctions |
React Server Functions support. |
baseUrl |
Serve the exported website under a subpath. |
turboModules |
Enable Turbo Modules (disables remote debugging). |
autolinkingModuleResolution |
Apply autolinking results to Metro module resolution (helps monorepos). |
onDemandFilesystem |
Expo's on-demand filesystem for Metro bundling outside watch folders. |
5.2 iOS configuration (ios)
| Key | Description |
|---|---|
bundleIdentifier |
iOS bundle identifier (unique on the App Store). |
buildNumber |
Build number (CFBundleVersion). |
appleTeamId |
Apple development team ID for all native targets. |
deploymentTarget |
Minimum iOS version, e.g. "18.6". |
supportsTablet |
Whether the app supports iPad screen sizes (default false). |
isTabletOnly / requireFullScreen |
iPad-only / disable Slide Over & Split View. |
icon |
iOS icon path or { light, dark, tinted } variants. |
userInterfaceStyle |
Force iOS light/dark/automatic appearance. |
infoPlist |
Arbitrary keys merged into the native Info.plist. |
entitlements |
Arbitrary keys merged into the native *.entitlements. |
privacyManifests |
PrivacyInfo.xcprivacy definitions (required-reason APIs, tracking domains, collected data types). |
associatedDomains |
Associated Domains for universal links. |
usesAppleSignIn |
Enable Sign in with Apple. |
usesIcloudStorage |
Enable iCloud storage for DocumentPicker. |
usesBroadcastPushNotifications |
Enable the Push Notifications Broadcast capability. |
googleServicesFile |
Path to GoogleService-Info.plist for Firebase. |
config.googleMapsApiKey |
Google Maps iOS SDK key. |
config.usesNonExemptEncryption |
Sets ITSAppUsesNonExemptEncryption. |
bitcode |
Enable Bitcode (or name a single configuration). |
version / runtimeVersion |
iOS-specific overrides of the top-level values. |
5.3 Android configuration (android)
| Key | Description |
|---|---|
package |
Android application ID (unique on the Play Store). |
versionCode |
Integer Play Store version code; increment each release. |
adaptiveIcon |
{ foregroundImage, backgroundImage, backgroundColor, monochromeImage } for the adaptive launcher icon. |
icon |
Legacy/non-adaptive icon path. |
userInterfaceStyle |
Force Android light/dark/automatic appearance (requires expo-system-ui). |
permissions |
Permissions to add to AndroidManifest.xml during prebuild. |
blockedPermissions |
Permissions to remove from the merged manifest. |
intentFilters |
Custom intent filters (deep links / app links) with action, data, category, and autoVerify. |
googleServicesFile |
Path to google-services.json (auto-enables FCM). |
config.googleMaps.apiKey |
Google Maps Android SDK key. |
allowBackup |
Allow automatic Google Drive backup (default true). |
softwareKeyboardLayoutMode |
resize (default) or pan. |
predictiveBackGestureEnabled |
Opt into the Android 13+ predictive back gesture. |
version / runtimeVersion |
Android-specific overrides of the top-level values. |
Tip
Most permissions are added automatically by the SDK module you install (for example installing
expo-locationadds location permissions during prebuild). Useandroid.permissionsonly to add extras andblockedPermissionsto strip ones you don't want.
5.4 Web configuration (web)
| Key | Description |
|---|---|
output |
Export mode: single (SPA, default), static (per-route HTML), or server (HTML + API routes for a Node server). |
favicon |
Path to the favicon image. |
name / shortName |
Document title and PWA short name. |
themeColor / backgroundColor |
PWA theme and background colors. |
display |
PWA display mode: fullscreen, standalone, minimal-ui, browser. |
orientation |
Default PWA orientation. |
bundler |
metro or webpack (Metro is the default modern bundler). |
lang / dir / scope / startUrl |
Standard web-manifest fields. |
config.firebase |
Firebase web configuration consumed by Expo Firebase packages. |
5.5 Config plugins
The plugins array extends your native projects during prebuild without you editing native files. Each entry is either a plugin name or a [name, options] tuple:
{
"expo": {
"plugins": [
"expo-router",
["expo-camera", { "cameraPermission": "Allow $(PRODUCT_NAME) to access your camera." }],
["expo-build-properties", { "android": { "compileSdkVersion": 35 }, "ios": { "deploymentTarget": "15.1" } }]
]
}
}
Many SDK packages ship a config plugin that wires up the required native permissions, Info.plist keys, or Gradle properties. Authoring your own plugins is covered in the Admin Guide.
6. Expo Router (file-based routing)
Expo Router (expo-router) is a file-based router for React Native and web, built on React Navigation. Files in your app/ directory become routes automatically. It is enabled by adding "expo-router" to your plugins and using the Expo Router entry point.
6.1 File conventions
| File / pattern | Meaning |
|---|---|
app/index.tsx |
The route for /. |
app/profile.tsx |
The route for /profile. |
app/_layout.tsx |
A layout that wraps all routes in its directory (declare a Stack, Tabs, etc. here). |
app/[id].tsx |
A dynamic route; id is available via useLocalSearchParams(). |
app/(group)/ |
A route group; the parenthesized segment organizes files without adding a URL segment. |
app/+not-found.tsx |
Fallback screen for unmatched routes. |
app/+html.tsx |
Customizes the root HTML document for static web export. |
app/+native-intent.tsx |
Handles incoming native deep-link intents. |
app/+middleware.ts |
Server middleware for routes (server output). |
*+api.ts |
A server API route (server output). |
Note
These conventions are enforced by the router's route matcher. Special files are prefixed with
+, dynamic segments use[brackets], and groups use(parentheses).
6.2 Navigation components
| Export | Purpose |
|---|---|
Link |
Declarative navigation: <Link href="/profile">Profile</Link>. |
Redirect |
Redirect on render: <Redirect href="/login" />. |
Slot |
Render the current child route with no surrounding navigator. |
Stack |
A stack navigator layout. |
Tabs |
A tab navigator layout. |
Navigator |
Low-level custom navigator. |
ErrorBoundary |
Per-route error boundary (export from a route file). |
Sitemap / Unmatched |
Built-in development sitemap and unmatched-route screens. |
SplashScreen |
Control the splash screen from the router. |
6.3 Hooks and the imperative API
| Hook / API | Purpose |
|---|---|
useRouter() |
Returns the router object for imperative navigation. |
router |
Imperative navigation: router.push(), router.replace(), router.back(), router.navigate(). |
useLocalSearchParams() |
Params for the current route. |
useGlobalSearchParams() |
Params shared across the navigation tree. |
useSearchParams() |
URL search parameters. |
useSegments() |
The active route segments as an array. |
usePathname() |
The current pathname string. |
useNavigation() |
The underlying React Navigation object. |
useFocusEffect() / useIsFocused() |
Run effects when a screen gains/loses focus. |
useNavigationContainerRef() |
Ref to the navigation container. |
useLoaderData() |
Data returned by a route loader. |
useTheme() / ThemeProvider / DefaultTheme / DarkTheme |
Navigation theming. |
import { Link, Stack, useLocalSearchParams, router } from 'expo-router';
// app/_layout.tsx — declare a stack
export default function Layout() {
return <Stack />;
}
// app/post/[id].tsx — a dynamic route
export default function Post() {
const { id } = useLocalSearchParams<{ id: string }>();
return (
<>
<Link href="/">Home</Link>
{/* ...render post {id}... */}
</>
);
}
// Imperative navigation from anywhere
router.push('/post/42');
Tip
Enable
experiments.typedRoutesto get autocompletion and type-checking on everyhref, so a typo like/porfileis a compile-time error.
7. The Expo SDK (package catalog)
The Expo SDK is a large set of native modules you install individually with npx expo install. Each module exposes a JavaScript API and (where needed) a config plugin and required permissions. Every package below is part of SDK 56. Import each with its package name, e.g. import * as Location from 'expo-location'.
Important
Always install SDK packages with
npx expo install <package>so the version matches your SDK. After installing a module with native code, rebuild your development build (or runnpx expo prebuild) — Expo Go only contains the modules bundled into it.
7.1 Core & runtime
| Package | Purpose |
|---|---|
expo |
The Expo SDK meta-package and runtime (registerRootComponent, Expo Go helpers, re-exports of the Modules API). |
expo-modules-core |
The core of the Expo Modules architecture (event emitters, shared objects, native-module access). |
expo-constants |
System information that stays constant for the app's lifetime, plus access to your evaluated app.config via Constants.expoConfig. |
expo-router |
File-based router for React Native and web (see §6). |
expo-updates |
Fetches and manages remotely-hosted OTA updates to your JS bundle and assets. |
expo-dev-client |
Builds a customizable development client (launcher + dev menu) that replaces Expo Go for projects with native code. |
expo-splash-screen |
Keep the native splash screen visible until you choose to hide it. |
expo-status-bar |
A StatusBar component with Expo-friendly defaults. |
expo-system-ui |
Interact with system UI elements (root view background, appearance). |
7.2 Device, system & sensors
| Package | Purpose |
|---|---|
expo-device |
Physical device information (model, OS, year class). |
expo-application |
Native application info (app ID, name, build version) at runtime. |
expo-battery |
Battery level/state plus event listeners. |
expo-cellular |
Information about the user's cellular service provider. |
expo-network |
Device network info (IP, MAC, airplane-mode status). |
expo-network-addons |
Extra networking functionality. |
expo-sensors |
Accelerometer, gyroscope, magnetometer, and pedometer. |
expo-localization |
Native user localization information (locale, region, timezone). |
expo-brightness |
Get and set screen brightness. |
expo-screen-orientation |
Manage and observe device screen orientation. |
expo-screen-capture |
Prevent screen capture/recording and detect screenshots. |
expo-keep-awake |
Prevent the screen from sleeping while a component is rendered. |
expo-navigation-bar |
Modify and observe the Android navigation bar. |
expo-tracking-transparency |
App-tracking permission management (iOS ATT). |
7.3 Media & graphics
| Package | Purpose |
|---|---|
expo-camera |
Camera preview component; take photos, record video, scan barcodes. |
expo-image |
A cross-platform, performant image component with web support. |
expo-image-picker |
System UI for picking images/videos or capturing with the camera. |
expo-image-manipulator |
Resize, crop, and transform images on the local filesystem. |
expo-audio |
Cross-platform audio playback and recording. |
expo-video |
Cross-platform, performant video component with web support. |
expo-video-thumbnails |
Generate an image thumbnail from a video. |
expo-media-library |
Access the user's media library (photos/videos). |
expo-gl |
An OpenGL ES render target (GLView) implementing WebGL 2.0. |
expo-blur |
Native blur view (iOS) with an Android fallback. |
expo-linear-gradient |
A gradient view component. |
expo-mesh-gradient |
A SwiftUI MeshGradient view for React Native. |
expo-glass-effect |
A native iOS glass effect view. |
expo-symbols |
SF Symbols (iOS) and Material Symbols (Android/web). |
expo-live-photo |
Display iOS Live Photos. |
7.4 Location & maps
| Package | Purpose |
|---|---|
expo-location |
Read geolocation; poll the current location or subscribe to updates. |
expo-maps |
A Map component using Google Maps (Android) and Apple Maps (iOS). |
7.5 Storage & files
| Package | Purpose |
|---|---|
expo-file-system |
Access the local filesystem on the device. |
expo-sqlite |
A SQLite database persisted across app restarts. |
expo-secure-store |
Encrypt and securely store key-value pairs locally. |
expo-asset |
Download assets and pass them into other APIs. |
expo-clipboard |
Get and set clipboard content (Android, iOS, macOS, web). |
expo-document-picker |
System UI for selecting documents from device providers. |
expo-sharing |
Share files with other compatible apps. |
expo-blob |
A cross-platform Blob implementation for binary data. |
7.6 Authentication & security
| Package | Purpose |
|---|---|
expo-auth-session |
Browser-based OAuth/OpenID authentication. |
expo-apple-authentication |
"Sign in with Apple". |
expo-local-authentication |
FaceID/TouchID (iOS) and Fingerprint (Android) authentication. |
expo-crypto |
Cryptography primitives (Android, iOS, web). |
expo-standard-web-crypto |
A partial implementation of the W3C Crypto API. |
@expo/app-integrity |
Assert app integrity on mobile platforms. |
7.7 Notifications & background work
| Package | Purpose |
|---|---|
expo-notifications |
Fetch push tokens; present, schedule, receive, and respond to notifications. |
expo-task-manager |
Define tasks that can run in the background. |
expo-background-task |
Background Task APIs for Android and iOS. |
expo-background-fetch |
Background fetch API. |
7.8 Communication, hardware & system integration
| Package | Purpose |
|---|---|
expo-web-browser |
Open the system web browser; supports auth redirects (openAuthSessionAsync). |
expo-linking |
Create and open deep links universally. |
expo-mail-composer |
Compose emails using the OS UI. |
expo-sms |
Send SMS messages via the system UI. |
expo-contacts |
Access the phone's system contacts. |
expo-calendar |
Interact with system calendars, events, and reminders. |
expo-print |
Printing (Android and iOS AirPrint). |
expo-store-review |
In-app store review prompts. |
expo-speech |
Text-to-speech. |
expo-haptics |
Haptics (iOS), vibration (Android), Web Vibration API. |
expo-intent-launcher |
Launch Android intents (e.g. open a settings activity). |
expo-font |
Load custom fonts at runtime. |
expo-checkbox |
A basic boolean checkbox input. |
7.9 UI toolkit & app insights
| Package | Purpose |
|---|---|
@expo/ui |
A collection of native UI components. |
expo-app-metrics |
Collect app startup, frame rate, memory, and session metrics. |
expo-insights |
Insight into app usage data. |
expo-widgets |
Home-screen widgets. |
Note
The legacy
expo-avpackage has been split into the dedicatedexpo-audioandexpo-videopackages. Prefer those for new projects.
8. Common Workflows
8.1 Run on a physical device
- Install Expo Go from the App Store / Play Store (for SDK-only apps), or build a development build with
expo-dev-client. - Run
npx expo start. - Scan the QR code (Camera on iOS, Expo Go on Android), or press
a/i.
8.2 Use a development build
When your app includes custom native modules or third-party native libraries that aren't in Expo Go:
npx expo install expo-dev-client
npx expo run:android # or run:ios — builds a dev client locally
npx expo start --dev-client
8.3 Build for the app stores (EAS)
Local builds use npx expo run:ios/run:android. For cloud builds and store submission, use the separate EAS CLI:
npm install -g eas-cli
eas build --platform ios
eas submit --platform ios
8.4 Ship over-the-air updates
With expo-updates configured (the updates key in app.json), publish a new JS/asset bundle without an app-store release:
eas update --branch production --message "Fix checkout bug"
The app downloads the matching update on next launch according to your checkAutomatically and fallbackToCacheTimeout settings.
8.5 Read environment variables at runtime
Variables prefixed with EXPO_PUBLIC_ are inlined into your client bundle at build time and are readable as process.env.EXPO_PUBLIC_*:
# .env
EXPO_PUBLIC_API_URL=https://api.example.com
const apiUrl = process.env.EXPO_PUBLIC_API_URL;
Caution
EXPO_PUBLIC_variables are embedded in the client bundle and are not secret. Never put API secrets, tokens, or private keys in them.
8.6 Export and host the web app
# Static site (per-route HTML)
npx expo export --platform web # outputs ./dist
# Test a server build locally
npx expo serve
9. Diagnostics
npx expo-doctor
Run the project doctor to check for known issues (mismatched dependency versions, invalid config, common misconfigurations):
npx expo-doctor
npx expo install --check
Verify every installed package is at an SDK-compatible version; add --fix to correct mismatches.
npx expo config --type introspect
See the fully resolved configuration after all config plugins run — invaluable when a plugin isn't producing the native change you expect.
Tip
Set
EXPO_DEBUG=1(orDEBUG=expo:*) before a command to get verbose logs and full stack traces, e.g.EXPO_DEBUG=1 npx expo prebuild.
10. Troubleshooting
Error: external macro 'ExpoModulesMacros.OptimizedFunctionAttachedMacro' could not be found
Cause: A toolchain/macro incompatibility surfacing during native compilation of an Expo module (reported on certain Xcode versions when building React Native from source). (Tracked in the community issue tracker for expo-crypto on EAS.)
Solution: Build with a supported Xcode version for your SDK and avoid buildReactNativeFromSource unless required; clear caches (npx expo start --clear) and re-run npx pod-install.
Prevention: Keep Xcode and your SDK on a compatible pairing and run npx expo-doctor before building.
Error: custom metro.config.js reported by doctor when none exists
Cause: expo-doctor's Metro-config check can flag a custom Metro config in a project that never created one.
Solution: Confirm with npx expo config and your project tree; if you have no metro.config.js, the warning is informational.
Prevention: Keep expo-doctor updated (npx expo install expo-doctor).
Problem: a native module is undefined / "not linked" in Expo Go
Cause: Expo Go only contains the native modules bundled into it. A newly added module with native code won't exist there.
Solution: Build a development build (npx expo run:android/run:ios with expo-dev-client) and run npx expo start --dev-client.
Prevention: Use Expo Go only for SDK-provided modules; switch to a dev build as soon as you add custom native code.
Problem: Metro serves a stale bundle / cryptic resolution errors
Cause: Cached bundler artifacts after dependency or config changes.
Solution: Restart with npx expo start --clear (alias --reset-cache).
Prevention: Clear the cache after changing metro.config.js, babel.config.js, or upgrading the SDK.
Problem: a device on cellular can't reach the dev server
Cause: The default lan host requires the device to be on the same local network.
Solution: Start with npx expo start --tunnel to route through ngrok.
Prevention: Use --tunnel whenever the device and computer aren't on the same Wi-Fi.
Problem: importing from react-native breaks the web build
Cause: On web, importing a module from react-native instead of react-native-web breaks the alias. Expo prints a targeted error in development.
Solution: Import shared APIs from react-native only where they're web-compatible, and platform-split files (*.web.tsx) for web-specific code.
Prevention: Use platform extensions and verify the web bundle during development.
Problem: config-plugin changes don't appear in the native project
Cause: Native files are generated by prebuild; editing app.json alone doesn't regenerate them.
Solution: Re-run npx expo prebuild (add --clean to regenerate from scratch). Inspect the result with npx expo config --type introspect.
Prevention: Never hand-edit generated native files when using CNG — change the plugin/config and re-run prebuild.
11. Environment Variables Reference
These variables are read by the Expo CLI and tooling (from source).
| Variable | Effect |
|---|---|
EXPO_PUBLIC_* |
Inlined into the client bundle and readable as process.env.EXPO_PUBLIC_* at runtime. |
EXPO_DEBUG |
Enable verbose debug logging and full stack traces. |
EXPO_OFFLINE |
Run the CLI in offline mode (skip network requests). |
EXPO_NO_DOCTOR |
Skip the automatic doctor check in EAS-related flows. |
EXPO_TOKEN |
Personal access token for non-interactive/CI authentication. |
EXPO_DOCTOR_ENABLE_DIRECTORY_CHECK |
Enable the doctor's directory check. |
CI |
Standard CI flag; affects interactive prompts and output. |
RCT_METRO_PORT |
Override the Metro port. |
HTTP_PROXY / http_proxy / https_proxy |
Proxy configuration for CLI network requests. |
BROWSER / BROWSER_ARGS |
Browser to open for web/dev URLs. |
EDITOR / VISUAL |
Editor opened by "open in editor" actions. |
EXPO_OS |
The current platform context used by tooling. |
EXPO_NONINTERACTIVE |
Disable interactive prompts in module scripts. |
Note
Many additional
EXPO_*variables exist for narrow internal cases (updates fingerprinting, prebuild caching, autolinking). The table above lists the ones developers commonly set.
12. FAQ
Q: Do I have to use Expo Go?
No. Expo Go is a convenience for SDK-only apps. Real projects typically use a development build (expo-dev-client), which is your own app binary that can include any native code.
Q: Managed vs. bare — which am I using?
If you let Expo generate your native projects with prebuild (Continuous Native Generation) and don't commit ios//android/, you're using the managed CNG workflow. If you commit and hand-edit native folders, you're "bare." You can mix both — adopt Expo modules in any RN app with npx install-expo-modules.
Q: How do I add a native library that isn't in the SDK?
Install it, then build a development build (npx expo run:*). Autolinking includes supported native modules automatically. Libraries needing native config should provide a config plugin.
Q: What's the difference between app.json and app.config.js?
app.json is static. app.config.js/app.config.ts is a JavaScript module that can read environment variables and compute the config dynamically. If both exist, the JS config receives the static config and can extend it.
Q: How do I read values from my config at runtime?
Put them under extra in your config and read Constants.expoConfig.extra from expo-constants.
Q: Where do expo build and expo publish go?
They were removed. Use EAS (eas build, eas submit, eas update) from the eas-cli package.
Q: How do I pin SDK-compatible versions?
Always install with npx expo install. Run npx expo install --check to audit and --fix to correct versions.
Q: How do I make navigation type-safe?
Enable experiments.typedRoutes and use TypeScript; Expo Router will type every href.
Q: Why is my newly installed module undefined in Expo Go?
Because Expo Go can't contain arbitrary native code. Build and run a development build instead.
Q: How do I update the app without an app-store release?
Configure expo-updates and publish with eas update. The app fetches the matching update on launch.
Q: How do I lint and check my project quickly?
npx expo lint for ESLint and npx expo-doctor for common issues.
13. Quick Reference
# Create & run
npx create-expo-app@latest my-app
npx expo start # dev server (add -a/-i/-w, --tunnel, --clear, --dev-client)
# Native builds (local)
npx expo run:android
npx expo run:ios
npx expo prebuild --clean # regenerate ios/ and android/
# Dependencies
npx expo install <pkg> # SDK-compatible install
npx expo install --check --fix # audit & fix versions
# Export & serve
npx expo export --platform web # static/site/server output to ./dist
npx expo serve # host a server build locally
# Config & quality
npx expo config --type introspect
npx expo lint
npx expo-doctor
# Account
npx expo login | logout | register | whoami
| Concept | Where it lives |
|---|---|
| App configuration | app.json / app.config.js (the expo key) |
| Routes & screens | app/ directory (Expo Router) |
| Native customization | plugins array + config plugins (regenerated by prebuild) |
| Runtime config access | Constants.expoConfig (expo-constants) |
| OTA updates | updates config + expo-updates + eas update |
| Cloud builds/submits | eas-cli (eas build / eas submit) |