baseUrl functionality scope and production-only
baseUrl functionality is production-only and must be set before exporting. Changing the value requires re-exporting the website. Images and assets work automatically when using require or import; manually referenced URLs require manual baseUrl prepending.
npx expo export:web webpack deprecation
`npx expo export:web` for webpack is deprecated in SDK 50+. Use `npx expo export` with universal Metro instead. Migrate from Webpack to Expo Router. Options --dev and -c/--clear are available but the command is disabled if app.json sets `expo.web.bundler: 'metro'`.
npx expo prebuild generates native code
`npx expo prebuild` generates native source code (ios and android directories) before compiling. Expo Prebuild is Expo CLI's unique system for generating native code. See Expo Prebuild docs for more information.
npx expo lint for code quality
`npx expo lint` sets up ESLint with Expo-specific settings and runs `npx eslint` with optimized options. `npx expo lint --fix` automatically fixes linting issues. By default targets src, app, and components directories; pass custom files/directories as arguments.
npx expo lint file extensions and customization
`npx expo lint` lints .js, .jsx, .ts, .tsx, .mjs, .cjs files by default. Customize extensions with `--ext` flag (e.g., `npx expo lint --ext .ts,.tsx`). Pass extra ESLint arguments using `--` operator (e.g., `npx expo lint -- --no-error-on-unmatched-pattern`). For more customization, use `npx eslint` directly.
npx expo config evaluates app config
`npx expo config` evaluates app.json or app.config.js. Options: --full (include all project config data), --json (output JSON format), -t/--type (show specific config type).
App config types: public, prebuild, introspect
Three app config types are generated: `public` (manifest for OTA updates, like HTML head), `prebuild` (config for Expo Prebuild including async modifiers; only time config is not serializable), `introspect` (subset of prebuild showing in-memory modifications like Info.plist or AndroidManifest.xml changes).
npx expo install for version-compatible packages
React Native is not backwards compatible; packages need exact versions matching the installed react-native. `npx expo install` is a drop-in replacement for `npm install`, using a curated list of popular packages and known working version combinations. Example: `npx expo install expo-camera`. Install multiple packages: `npx expo install typescript expo-sms`. Pass underlying package manager arguments with `--` operator.
npx expo install version validation with --check and --fix
`npx expo install --check` checks which installed packages need updating and prompts to fix locally; exits with non-zero in CI for immutable validation. `npx expo install --fix` always fixes packages regardless of environment. Both can target specific packages: `npx expo install react-native expo-sms --check`. `npx expo install --fix` upgrades all packages.
Exclude packages from version validation
Exclude specific packages from `npx expo install` version checking by using the `expo.install.exclude` property in package.json.
npx expo install package manager support
`npx expo install` supports bun, npm, pnpm, and yarn. Auto-detection: bun.lockb/bun.lock (bun), package-lock.json (npm), pnpm-lock.yaml (pnpm), yarn.lock (yarn). Force with named arguments: --bun, --npm, --pnpm, --yarn.
npx expo customize generates default files
`npx expo customize` generates default project files: babel.config.js (Babel config; required for non-Expo CLI bundling), webpack.config.js (webpack config for web), metro.config.js (Metro config for universal development; required for npx react-native), tsconfig.json (TypeScript config with dependency installation).
Expo CLI environment variables reference
Comprehensive table of Expo CLI environment variables and their types/descriptions: HTTP_PROXY (string, HTTP/HTTPS proxy URL), EXPO_NO_WEB_SETUP (boolean, prevent forcing web dependencies), EXPO_OFFLINE (boolean, skip network requests), EXPO_NO_TYPESCRIPT_SETUP (boolean, prevent TypeScript config on start), DEBUG=expo:* (string, enable debug logs), EXPO_DEBUG (boolean, alias for DEBUG=expo:*), EXPO_PROFILE (boolean, enable CLI profiling), EXPO_NO_CACHE (boolean, disable global caching), CI (boolean, disable interactivity in CI), EXPO_NO_TELEMETRY (boolean, disable telemetry), EXPO_NO_GIT_STATUS (boolean, skip git status warnings), EXPO_NO_REDIRECT_PAGE (boolean, disable redirect page), EXPO_PUBLIC_FOLDER (string, public directory for web), EDITOR (string, editor name for O key), EXPO_EDITOR (string, Expo-specific editor override), EXPO_IMAGE_UTILS_NO_SHARP (boolean, use Jimp instead of Sharp), EXPO_TUNNEL_SUBDOMAIN (boolean/string, set tunnel subdomain experimentally), EXPO_METRO_NO_MAIN_FIELD_OVERRIDE (boolean, force metro resolver fields), EXPO_NO_INSPECTOR_PROXY (deprecated boolean), EXPO_NO_CLIENT_ENV_VARS (boolean, prevent inlining EXPO_PUBLIC_), EXPO_NO_DOTENV (boolean, prevent .env loading), EXPO_NO_METRO_LAZY (boolean, disable lazy imports), EXPO_USE_TYPED_ROUTES (boolean, enable typed routes), EXPO_METRO_UNSTABLE_ERRORS (deprecated boolean), EXPO_USE_METRO_WORKSPACE_ROOT (deprecated boolean), EXPO_NO_METRO_WORKSPACE_ROOT (boolean, disable workspace root detection), EXPO_USE_UNSTABLE_DEBUGGER (deprecated boolean), EXPO_ADB_USER (string, ADB user number for multi-profile Android), EXPO_NO_TELEMETRY_DETACH (boolean, send telemetry in main thread), EXPO_UNSTABLE_ATLAS (deprecated experimental, gather bundle info), EXPO_ATLAS (boolean SDK 53+, gather bundle info), EXPO_NO_BUNDLE_SPLITTING (experimental boolean, disable web chunk splitting), EXPO_USE_METRO_REQUIRE (boolean SDK 52+, enable custom Metro require), EXPO_UNSTABLE_METRO_OPTIMIZE_GRAPH (experimental boolean, eager bundling), EXPO_UNSTABLE_TREE_SHAKING (experimental boolean, enable tree shaking), EXPO_NO_REACT_NATIVE_WEB (deprecated boolean SDK 56+), EXPO_NO_DEPENDENCY_VALIDATION (boolean, disable validation), EXPO_WEB_DEV_HYDRATE (boolean, enable web hydration in dev), EXPO_UNSTABLE_LIVE_BINDINGS (experimental boolean SDK 54+, disable live binding), EXPO_UNSTABLE_LOG_BOX (experimental boolean SDK 55+, enable LogBox), EXPO_NO_QR_CODE (boolean, prevent QR code display).
Expo Structured Field Values protocol subset
Expo maintains a custom version of Structured Field Values for HTTP (IETF RFC 8941) that implements only a subset of the full protocol. The implemented subset includes: all key values, string items, integer items, decimal items, and dictionaries.
Building iOS Expo Go
To build Expo Go for iOS: run `pod install` in the apps/expo-go/ios directory, then open and run ios/Exponent.xcworkspace in Xcode.
Expo Go pre-built installation
To install Expo Go on a simulator or device without building from source, visit expo.dev/go.
macOS requirement for Expo Go building
Expo Go can only be built on macOS.
Environment setup for Expo Go development
To set up the environment for developing Expo Go: install direnv and Homebrew; clone the repo with `git clone --recurse-submodules` to a path without spaces; run `brew bundle` in the root directory; run `pnpm install` in the root directory; run `pnpm setup:native` in the root directory; run `pnpm build` in the packages/expo directory.
Building Android Expo Go
To build Expo Go for Android, run `./gradlew app:assembleDebug` in the apps/expo-go/android directory.
React Native setup for Expo Go building
To set up React Native for Expo Go building, run `pnpm install:react-native-lab` in the monorepo root. Optionally, you can build the React Native Android dependency using `./gradlew :packages:react-native:ReactAndroid:buildCMakeDebug` in the react-native-lab/react-native directory to help narrow down potential issues.
Running Metro for Native Component List
To run Metro for the Native Component List: navigate to the apps/native-component-list directory, then run `EXPO_SDK_VERSION=UNVERSIONED npx expo start --clear`. Use the built Expo Go app to scan the QR code or press 'i' or 'a' to open the Native Component List.
Clearing C++ build artifacts in Expo Go
If you encounter C++ related errors when building Expo Go, run `find . -name ".cxx" -type d -prune -exec rm -rf '{}' +` to clear .cxx build artifacts.
Cleaning Expo Go Android project
To clean the Expo Go Android project, run `./gradlew clean` in the apps/expo-go/android directory.
Complete rebuild of Expo Go with git clean
For a complete rebuild of Expo Go, run `git submodule foreach --recursive git clean -xfd` and/or `git clean -xfd` to remove all untracked files, then run `./scripts/download-dependencies.sh` and rebuild. This approach is effective for resolving persistent build issues, though it takes longer than incremental builds.
Lifecycle listener implementation in Package classes
To create lifecycle listeners, implement the appropriate method in a derived Package class:
1. **ReactActivityLifecycleListener**: Implement createReactActivityLifecycleListeners(Context activityContext) to return a List of ReactActivityLifecycleListener instances.
2. **ApplicationLifecycleListener**: Implement createApplicationLifecycleListeners(Context context) to return a List of ApplicationLifecycleListener instances.
Both methods take a Context parameter and return a list of their respective listener instances.
expo-calendar listEvents for single and multiple calendars
List events using the listEvents method with the following signatures:
- Single calendar: listEvents(calendarId, startDate, endDate) - list events in one calendar
- Multiple calendars: listEvents([calendar1, calendar2, ...], startDate, endDate) - list events across multiple calendars
These methods replace the legacy getEventsAsync function:
- Old: getEventsAsync([calendarId], startDate, endDate)
- New: listEvents(calendarId, startDate, endDate)
Or for multiple calendars:
- Old: getEventsAsync([id1, id2], startDate, endDate)
- New: listEvents([calendar1, calendar2], startDate, endDate)
expo-calendar static get methods for ExpoCalendarEvent and ExpoCalendarReminder
The expo-calendar module provides static get methods to obtain instances from IDs:
- ExpoCalendarEvent.get(eventId): Returns an ExpoCalendarEvent instance from an event ID. This replaces the legacy getEventAsync(eventId).
- ExpoCalendarReminder.get(reminderId): Returns an ExpoCalendarReminder instance from a reminder ID. This replaces the legacy getReminderAsync(reminderId). iOS only.
React Native WebView platform compatibility
React Native WebView is compatible with iOS, Android, Windows, and macOS. It supports both the old architecture (paper) and the new architecture (fabric). This project is compatible with Expo.
WebView basic usage example
Import the WebView component from 'react-native-webview' and use it like this:
```tsx
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { WebView } from 'react-native-webview';
const MyWebComponent = () => {
return <WebView source={{ uri: 'https://reactnative.dev/' }} style={{ flex: 1 }} />;
}
```
WebView common issue: RNCWebView native component does not exist
If you receive the error 'Invariant Violation: Native component for "RNCWebView does not exist"', it likely means you forgot to run `react-native link` or there was an error with the linking process.
WebView common issue: mergeDexRelease build error on Android
If you encounter a build error during the task `:app:mergeDexRelease`, you need to enable multidex support in `android/app/build.gradle`.
React Native WebView is community-maintained replacement for built-in WebView
React Native WebView is a community-maintained WebView component for React Native. It is intended to be a replacement for the built-in WebView which was removed from React Native core.
ViewShot capture mode behavior and image loading
When using captureMode='mount', image loading is not automatically waited. If the view contains images that need to load, use captureMode='none' with viewShotRef.capture() called after Image#onLoad callback instead.
Platform-specific component support for snapshots
Component support for snapshots varies by platform: View, Text, Image are supported on iOS, Android, and Windows. WebView is supported on iOS, Android (with View collapsable={false} wrapper), and Windows. gl-react v2 only works on iOS. react-native-video is not supported on any platform. react-native-maps works on iOS only. react-native-svg works on iOS and Android. react-native-camera only works on Android. Windows lacks support for several components.
Capturing Android Views requires collapsable={false}
On Android, when snapshotting a View, you must set collapsable={false} on the view. Some content may need to be wrapped in <View collapsable={false}> to be snapshotable. The ViewShot component automatically sets collapsable={false}.
Snapshot timing requirements to avoid zero-size errors
Do not snapshot instantly. Wait for at least the first onLayout event, use a timeout, or wait for Image onLoad if present. The view must have width and height > 0. The ViewShot component automatically waits for the first onLayout event.
RAW image format for performance optimization
RAW is a new image format representing an ARGB array of pixels. It has no compression, making screenshots take less than 16ms. RAW format is supported for zip-base64, base64, and tmpfile result types. RAW files on disk are saved as '${width}:${height}|${base64}' string.
Snapshot image may be larger than specified dimensions
If a snapshotted image does not match specified width and height but is twice or three times bigger, this is because the snapshot result is in real pixel size while React Native style dimensions are in 'point' units. Use the width and height options in captureRef to force a resize, though this may affect image quality.
Sharing capture results with expo-sharing
When sharing capture results with expo-sharing, use tmpfile or the default capture result. Prepend 'file://' to the result before calling shareAsync.
Background color recommended for snapshots to avoid transparency issues
Use a background color on the view being rasterized to avoid transparent pixels and potential weirdness with borders appearing around text.
GL Surface View capture on Android requires handleGLSurfaceViewOnAndroid prop
To properly capture GL Surface Views in the view tree on Android, use the handleGLSurfaceViewOnAndroid prop set to true. It is false by default because it can have significant performance impact.
zip-base64 format for fast compressed snapshots
zip-base64 is a result format that applies zip/deflate compression to screenshot results and converts them to base64. In combination with RAW format, this provides a super-fast approach for capturing and delivering screen views to React side.
react-native-view-shot installation in Expo
In Expo projects, install react-native-view-shot using 'expo install react-native-view-shot'. For iOS, ensure CocoaPods are installed by running 'npx pod-install'.
ViewShot component props reference
ViewShot component accepts the following props: children (the content to rasterize), options (same as captureRef method options), captureMode (default undefined for manual capture; 'mount' for automatic capture on mount; 'continuous' experimental for continuous capture; 'update' experimental for capture on React redraws), onCapture (callback when captureMode is defined), and onCaptureFailure (callback when capture fails).
captureRef method signature and options
captureRef(view, options) is a lower-level imperative API that returns a Promise of the image URI. The view parameter is a reference to a React Native component. Options include: fileName (string, Android only, minimum 3 characters), width/height (number, resized dimensions), format (string: 'png', 'jpg', or 'webm' on Android; defaults to 'png'), quality (number 0.0-1.0, for lossy formats), result (string: 'tmpfile' default, 'base64', 'data-uri', or 'zip-base64'), snapshotContentContainer (bool, for ScrollView content container height), and useRenderInContext (bool, iOS only, uses renderInContext instead of drawViewHierarchyInRect).
captureScreen method for Android and iOS
captureScreen() captures the currently displayed screen as a hardware screenshot without requiring a ref input. It does not capture ScrollView content in entirety, only visible portions. It accepts the same options as captureRef and returns a Promise of the image URI. Available on Android and iOS only.
releaseCapture method for managing captured URIs
releaseCapture(uri) releases a previously captured URI. For tmpfile results, it cleans up the files. For other result types, it has no effect. Tmpfile captures are automatically cleaned when the app closes. ViewShot component automatically uses this when capturing multiple times to prevent file leaks.
Public assets in DOM components require EXPO_BASE_URL
Contents of the root public directory are copied to the native app binary. Reference public assets in DOM components using the process.env.EXPO_BASE_URL prefix, like <img src={`${process.env.EXPO_BASE_URL}img.png`} />. Public assets are not supported in EAS Update; use require() instead.
Manual WebView with raw HTML
Create a manual WebView for rendering websites from a remote server using the WebView component from react-native-webview with raw HTML, like <WebView source={{ html: '<h1>Hello, world!</h1>' }} />.
DOM components are single-page applications
DOM components render as single-page applications with no server-side rendering (SSR) or static site generation (SSG). Search engine optimization is unnecessary for embedded JavaScript code.
DOM component data bridge limitations
Data can be sent between DOM components and native components only through an asynchronous JSON transport system. Global state is not accessible across JavaScript engines. Avoid relying on data across JS engines and deep linking to nested URLs in DOM components, as they do not support full reconciliation with Expo Router.
Limitations: no children, no native views, no OTA updates
DOM components cannot accept children props. Native views cannot be added to DOM components. DOM components currently do not support OTA updates. Function props must be asynchronous and cannot return values synchronously.
Secure context in DOM components for release and debug builds
In release builds, DOM components served using the file:// scheme are provided a secure context by default. In debug builds, use tunneling to serve DOM components over HTTPS by running npx expo start --tunnel with expo-dev-client installed.
Example: Using DOM component in native app
```tsx App.tsx (native)
import DOMComponent from './my-component.tsx';
export default function App() {
return (
<DOMComponent name="Europa" />
);
}
```
This example shows how to import and use a DOM component marked with 'use dom' in a native app.
Example: WebView props via dom prop
```tsx App.tsx (native)
import DOMComponent from './my-component';
export default function App() {
return (
<DOMComponent
dom={{
scrollEnabled: false,
}}
/>
);
}
```
This example shows how to pass WebView props to a DOM component using the dom prop.
Example: Native action function in DOM component
```tsx App.tsx (native)
import DomComponent from './my-component';
export default function App() {
return (
<DomComponent
hello={(data: string) => {
console.log('Hello', data);
}}
/>
);
}
```
```tsx my-component.tsx (web)
'use dom';
export default function MyComponent({ hello }: { hello: (data: string) => Promise<void> }) {
return <p onClick={() => hello('world')}>Click me</p>;
}
```
This example shows passing an asynchronous native action from native to DOM component and calling it from the DOM.
Example: Using useDOMImperativeHandle with ref (SDK 53+)
```tsx my-component.tsx (web)
'use dom';
import { useDOMImperativeHandle, type DOMImperativeFactory } from 'expo/dom';
import { Ref, useRef } from 'react';
export interface DOMRef extends DOMImperativeFactory {
focus: () => void;
}
export default function MyComponent(props: {
ref: Ref<DOMRef>;
dom?: import('expo/dom').DOMProps;
}) {
const inputRef = useRef<HTMLInputElement>(null);
useDOMImperativeHandle(
props.ref,
() => ({
focus: () => {
inputRef.current?.focus();
},
}),
[]
);
return <input ref={inputRef} />;
}
```
In SDK 53+, the ref is passed as a prop directly and can be used with useDOMImperativeHandle.
Example: Measuring DOM component with ResizeObserver
```tsx my-component.tsx (web)
'use dom';
import { useEffect } from 'react';
function useSize(callback: (size: { width: number; height: number }) => void) {
useEffect(() => {
const observer = new ResizeObserver(entries => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
callback({ width, height });
}
});
observer.observe(document.body);
callback({
width: document.body.clientWidth,
height: document.body.clientHeight,
});
return () => {
observer.disconnect();
};
}, [callback]);
}
export default function DOMComponent({
onDOMLayout,
}: {
dom?: import('expo/dom').DOMProps;
onDOMLayout: (size: { width: number; height: number }) => void;
}) {
useSize(onDOMLayout);
return <div style={{ width: 500, height: 500, background: 'blue' }} />;
}
```
This example shows observing size changes in a DOM component and reporting them back to the native side.
Features of DOM components
DOM components support: shared bundler config across web, native, and DOM components; React, TypeScript, CSS, and all Metro features; logging in terminal and Safari/Chrome debugging; Fast Refresh and HMR; embedded exports for offline support; unified assets across web and native; introspection in Expo Atlas; access to all web functionality without native rebuild; runtime error overlay in development; and Expo Go support.
'use dom' directive for React DOM in native apps
The 'use dom' directive enables rendering React DOM components in Expo native apps. Add 'use dom'; at the top of a web component file to mark it as a DOM component that will render HTML elements like <div>, <img>, and other web APIs directly in the native app.