Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Expo — User Guide

Generated June 2026 from expo/expo at 00259c2

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

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/expo monorepo 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:android or prebuild), 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 expo package is installed and configured, you can add any Expo SDK package (for example npx 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 separate eas-cli package — not by the local expo CLI. Older expo build/expo publish commands 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-client when your project contains custom native modules. Use --go to force Expo Go even if expo-dev-client is 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/ and android/ as generated output. Re-running prebuild --clean discards 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_TOKEN environment variable to a personal access token instead of running expo 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.js when 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 in extra are readable at runtime via Constants.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-location adds location permissions during prebuild). Use android.permissions only to add extras and blockedPermissions to 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.typedRoutes to get autocompletion and type-checking on every href, so a typo like /porfile is 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 run npx 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-av package has been split into the dedicated expo-audio and expo-video packages. Prefer those for new projects.


8. Common Workflows

8.1 Run on a physical device

  1. Install Expo Go from the App Store / Play Store (for SDK-only apps), or build a development build with expo-dev-client.
  2. Run npx expo start.
  3. 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 (or DEBUG=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)