Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions docs/platforms/react-native/manual-setup/expo.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,20 @@ To verify that everything is working as expected, build the `Release` version of

## Next Steps

- [Learn how to upload source maps for native builds and Expo Updates](/platforms/react-native/sourcemaps/uploading/expo/)
- [Add automatic tracing with Expo Router](/platforms/react-native/tracing/instrumentation/expo-router/)
- [Configure the Sentry Android Gradle Plugin](/platforms/react-native/manual-setup/expo/gradle/)
### Expo-Specific Features

- [Expo Updates](/platforms/react-native/manual-setup/expo/expo-updates/) — Automatic OTA update context, searchable tags, and emergency launch alerts
- [EAS Build Hooks](/platforms/react-native/manual-setup/expo/eas-build-hooks/) — Capture build failures and lifecycle events from EAS Build
- [Sentry Android Gradle Plugin](/platforms/react-native/manual-setup/expo/gradle/) — Advanced Android build configuration

### Source Maps and Releases

- [Upload source maps for native builds and Expo Updates](/platforms/react-native/sourcemaps/uploading/expo/)

### Performance

- [Expo Router tracing](/platforms/react-native/tracing/instrumentation/expo-router/) — Navigation transitions, performance spans, and prefetch instrumentation
- [Expo Image and Asset tracing](/platforms/react-native/tracing/instrumentation/expo-resources/) — Automatic spans for `expo-image` and `expo-asset`

## Notes

Expand Down
79 changes: 79 additions & 0 deletions docs/platforms/react-native/manual-setup/expo/eas-build-hooks.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
title: EAS Build Hooks
description: "Capture EAS build failures and lifecycle events in Sentry."
sidebar_order: 10
---

EAS Build runs your app builds on Expo's infrastructure. When a build fails, the error can be hard to diagnose — it happens outside your local environment and outside your app. EAS Build Hooks let you send build lifecycle events directly to Sentry so you can track failures, correlate them with releases, and get notified when production builds break.

## Setup

Add the Sentry EAS build hook scripts to your `package.json`:

```json {filename:package.json}
{
"scripts": {
"eas-build-on-error": "sentry-eas-build-on-error",
"eas-build-on-success": "sentry-eas-build-on-success",
"eas-build-on-complete": "sentry-eas-build-on-complete"
}
}
```

EAS Build automatically runs `package.json` scripts with these names at the corresponding lifecycle events. See the [Expo npm hooks documentation](https://docs.expo.dev/build-reference/npm-hooks/) for more details.

Set your DSN as an EAS secret or environment variable so it's available during builds:

```bash
eas secret:create --scope project --name SENTRY_DSN --value <your-dsn>
```

## What Gets Captured

**`eas-build-on-error`** — Sends an `EASBuildError` event to Sentry when a build fails. The event includes:

- Build platform (`ios` / `android`)
- Build profile (for example, `production`, `preview`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I'm not super familiar with expo builds, is this the same thing as the build environment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- Build ID, project ID, git commit hash
- Whether the build ran on CI

**`eas-build-on-success`** — Optionally sends an info event when a build succeeds. Disabled by default; enable it with `SENTRY_EAS_BUILD_CAPTURE_SUCCESS=true`.

**`eas-build-on-complete`** — A single hook that captures either a failure or success event depending on the build outcome. Use this instead of `on-error` + `on-success` if you prefer a single hook entry point.

All events are tagged with `eas.*` tags so you can filter and group them in Sentry.

## Environment Variables

| Variable | Required | Description |
|---|---|---|
| `SENTRY_DSN` | Yes | Your project's DSN |
| `SENTRY_EAS_BUILD_CAPTURE_SUCCESS` | No | Set to `true` to capture successful builds |
| `SENTRY_EAS_BUILD_TAGS` | No | JSON object of additional tags, for example `{"team":"mobile"}` |
| `SENTRY_EAS_BUILD_ERROR_MESSAGE` | No | Custom error message for failed builds |
| `SENTRY_EAS_BUILD_SUCCESS_MESSAGE` | No | Custom message for successful builds |
| `SENTRY_RELEASE` | No | Override the release name (defaults to `{appVersion}+{buildNumber}`) |

## Environment Files

The hook automatically loads environment variables from the following sources (without overwriting values already set by EAS):

1. `@expo/env` (if available)
2. `.env` file in the project root (via `dotenv`, if available)
3. `.env.sentry-build-plugin` file in the project root

This means you can store the DSN in `.env.sentry-build-plugin` locally, and use an EAS secret in CI — the hook will use whichever value is already set.

## Using `on-complete` Instead of Separate Hooks

If you only need to track failures but want the option to add success tracking later, `eas-build-on-complete` is the most flexible option. It reads `EAS_BUILD_STATUS` (set by EAS) to determine the outcome and captures the appropriate event:

```json {filename:package.json}
{
"scripts": {
"eas-build-on-complete": "sentry-eas-build-on-complete"
}
}
```

To also capture successful builds, set `SENTRY_EAS_BUILD_CAPTURE_SUCCESS=true` in your build environment.
74 changes: 74 additions & 0 deletions docs/platforms/react-native/manual-setup/expo/expo-updates.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
title: Expo Updates
description: "Automatic context and alerts for Expo OTA updates."
sidebar_order: 20
---

When you ship OTA updates with Expo Updates, events captured by Sentry are automatically enriched with information about which update is running. This lets you filter issues by update channel, runtime version, or update ID — and get alerted when an emergency launch occurs.

These integrations are enabled by default in Expo projects. No additional setup is required.

## Expo Updates Context

Every Sentry event includes an `ota_updates` context with the current update state:

| Field | Description |
|---|---|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

| `update_id` | UUID of the currently running OTA update |
| `channel` | Expo Updates channel (for example, `production`, `staging`) |
| `runtime_version` | Runtime version of the update |
| `is_enabled` | Whether Expo Updates is enabled |
| `is_embedded_launch` | Whether the app launched from the embedded bundle |
| `is_using_embedded_assets` | Whether the app is using embedded assets |
| `is_emergency_launch` | Whether this was an emergency launch (fallback to embedded) |
| `emergency_launch_reason` | Reason for the emergency launch (if applicable) |
| `check_automatically` | Automatic update check policy |
| `launch_duration` | How long the update launch took (ms) |
| `created_at` | When the update was created |

## Searchable Tags

The SDK also sets the following tags on every event so you can filter and group issues in Sentry:

| Tag | Value |
|---|---|
| `expo.updates.update_id` | UUID of the running update |
| `expo.updates.channel` | Update channel name |
| `expo.updates.runtime_version` | Runtime version string |

To find all issues from a specific update channel, use `expo.updates.channel:production` in the Sentry issue search.

## Emergency Launch Warnings

Expo Updates performs an _emergency launch_ when it fails to load the latest OTA update and falls back to the embedded bundle. This can silently degrade user experience.

When the SDK detects an emergency launch at startup, it automatically sends a `warning`-level event to Sentry with:

- Message: `Expo Updates emergency launch: <reason>` (or `Expo Updates emergency launch` if no reason is available)
- Tag: `expo.updates.emergency_launch: true`

You can set up a Sentry alert on this tag to get notified whenever an emergency launch happens in production.

<Alert>

Emergency launch detection only runs in native builds, not in Expo Go.

</Alert>

## Expo Constants Context

The SDK automatically captures Expo Constants as an `expo_constants` context on every event. This provides metadata about the execution environment and app configuration:

| Field | Description |
|---|---|
| `execution_environment` | Where the app is running (`storeClient`, `standalone`, `bare`) |
| `app_ownership` | Whether the app runs in Expo Go or standalone |
| `debug_mode` | Whether debug mode is enabled |
| `expo_version` | Expo client version |
| `expo_runtime_version` | Runtime version from app config |
| `session_id` | Current session ID |
| `app_name` | App name from `app.json` |
| `app_slug` | App slug from `app.json` |
| `app_version` | App version from `app.json` |
| `expo_sdk_version` | Expo SDK version |
| `eas_project_id` | EAS project ID |
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: Expo Image and Asset Instrumentation
description: "Automatic performance spans for expo-image and expo-asset."
sidebar_order: 70
---

Image and asset loading times directly affect how quickly your app feels responsive. The SDK can automatically create performance spans for `expo-image` and `expo-asset` operations so you can see exactly how long prefetching and loading takes within your existing traces.

## Expo Image

Wrap the `Image` class from `expo-image` once at app startup to instrument `Image.prefetch()` and `Image.loadAsync()`:

```javascript {filename:App.js}
import { Image } from 'expo-image';
import * as Sentry from '@sentry/react-native';

Sentry.wrapExpoImage(Image);
```

After wrapping, every call to `Image.prefetch()` creates a `resource.image.prefetch` span, and every call to `Image.loadAsync()` creates a `resource.image.load` span. The spans are automatically linked to the active trace.

Span attributes include:

- `image.url` — the image URL (for single-image operations)
- `image.url_count` — number of URLs (for batch prefetch)

## Expo Asset

Wrap the `Asset` class from `expo-asset` once at app startup to instrument `Asset.loadAsync()`:

```javascript {filename:App.js}
import { Asset } from 'expo-asset';
import * as Sentry from '@sentry/react-native';

Sentry.wrapExpoAsset(Asset);
```

After wrapping, every call to `Asset.loadAsync()` creates a `resource.asset` span with an `asset.count` attribute.

## Notes

- Both `wrapExpoImage` and `wrapExpoAsset` are safe to call multiple times — the SDK guards against double-wrapping.
- Spans are only created when a trace is active. If you're not using [tracing](/platforms/react-native/tracing/), no spans are created and there's no overhead.
- `expo-image` and `expo-asset` are peer dependencies — the SDK does not require them to be installed.
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,34 @@ This option will enable automatic measuring of the time to initial display for e

This ensures that transactions that are from routes that've been seen and don't have any spans, are not being sampled. This removes a lot of clutter, making it so that most back navigation transactions are now ignored. The default value is `true`.

## Prefetch Instrumentation

<Alert level="info">

`router.prefetch()` requires **Expo Router v5 (Expo SDK 53) or later**. On older versions the method does not exist; calling it will throw a runtime error.

</Alert>

Expo Router's `router.prefetch()` preloads a route before the user navigates to it. By default, these prefetch calls are invisible in your traces. Wrapping the router with `Sentry.wrapExpoRouter()` adds an automatic `navigation.prefetch` span for each call so you can see prefetch timing alongside your other navigation spans.

```javascript {filename:app/(tabs)/index.tsx}
import { useRouter } from 'expo-router';
import * as Sentry from '@sentry/react-native';

function HomeScreen() {
const router = Sentry.wrapExpoRouter(useRouter());

return (
<Button
title="Go to Details"
onPress={() => router.prefetch('/details')}
/>
);
}
```

The span name is `Prefetch /details` (or `Prefetch unknown` for unresolvable routes). Span attributes include `route.href` and `route.name`.

## Notes

- Slow and Frozen frames, Time To Initial Display and Time To Full Display are only available in native builds, not in Expo Go.
Loading