AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
EAS Observe is an Expo Application Services tool designed to track application performance, including startup times, navigation metrics, and custom events. It also provides robust error and crash reporting capabilities. By integrating the library into an Expo project, developers can gain visibility into real-world app behavior, helping to identify bottlenecks, diagnose slow startups, and resolve runtime issues. The service includes CLI tools for querying metrics and supports various integrations with core Expo libraries to provide deeper insights into image loading and animation performance.
What it does
- Tracks startup performance metrics like cold and warm launch times.
- Measures time-to-interactive and navigation performance.
- Captures JavaScript errors and native crashes with symbolicated stack traces.
- Supports user-defined events for custom performance tracking.
- Provides CLI commands to query and analyze performance data.
- Integrates with Expo Router, React Navigation, and other libraries for contextual metrics.
When to use it
- When you need to monitor production app performance and startup speed.
- When you want to track and debug runtime errors and native crashes.
- When investigating performance regressions across different devices or network conditions.
- When you need to measure the impact of specific user flows or navigation patterns.
Advantages
- Provides actionable insights into real-world user experience.
- Helps distinguish between code-related slowness and environmental throttling.
- Simplifies error tracking with integrated crash reporting.
- Offers granular control over data sampling and reporting configuration.
Usage examples
- “How do I add EAS Observe to my Expo project?”
- The agent will guide you through installing expo-observe, wrapping your root layout with ObserveRoot, and configuring the necessary integrations.
- “How can I check my app's cold launch performance from the terminal?”
- The agent will provide the appropriate eas observe:metrics-summary command to retrieve and display your application's startup metrics.
- “What does it mean if my app has high TTI but low totalDelay?”
- The agent will explain that this indicates a slow but smooth launch, suggesting you should look into bundle size or data-fetch waterfalls.
- “How do I enable the Reanimated integration for EAS Observe?”
- The agent will show you how to update your Observe.configure call to include the reanimated integration.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/expo/eas-observe/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip eas-observe.zip -d .agents/skills
Documentation by Expo
EAS Observe
EAS service - costs apply. EAS Observe is an Expo Application Services product. The free EAS plan allows up to 10,000 monthly active users, with a limited set of features; higher usage requires a paid subscription. For details, see https://expo.dev/pricing#plan-features.
EAS Observe tracks startup, navigation, and custom-event performance from production Expo apps, and records JavaScript errors and native crashes. It needs a development or production build — the native library is not in Expo Go.
Source of truth: https://docs.expo.dev/eas/observe/ — always consult the canonical docs when API details matter, especially get-started, configuration, integrations, and the metrics reference. EAS Observe is evolving; this skill's references are written to stay accurate but may lag the docs.
Which reference to read
The five reference files in ./references/ cover what people typically need this skill for:
Adding EAS Observe to a project →
./references/setup.md. Install, wrap the root layout (AppMetricsRooton SDK 55,ObserveRooton SDK 56+), mark the app interactive (globalmarkInteractive()on SDK 55, theuseObserve()hook or<ObserveInteractiveMarker />on SDK 56+), optional per-route navigation metrics through the Expo Router / React Navigation integrations, user-defined events viaObserve.logEvent(SDK 56+), error reporting, and runtime configuration (sampling, dispatch, environments, custom endpoint).Querying metrics from the terminal →
./references/queries.md. Theeas observe:*commands —metrics-summary,metrics,routes,events,session,versions,errors,event— with flags, metric aliases, table layouts, JSON shapes, and common workflows. Also the matchingobserve_*tools in the Expo MCP Server, and how their inputs differ from the CLI flags.Reading a dashboard or CLI output →
./references/metrics.md. Target thresholds per metric, what the automatic TTI params mean (frameRate.*,device.*,network.*), and diagnostic patterns for telling slow-but-smooth startup apart from main-thread contention, hard blocks, or throttled devices.Reporting problems from library integrations →
./references/library-integrations.md. The current and future integrations that report problems a library detects as events or errors, with their options, limits, and how to query the reports. Currently:expo-image(expo-image.oversizedevents for images decoded far larger than the screen) andreact-native-reanimated(reanimated.errorerrors and optionalreanimated.warningevents).Shipping an Observe integration in a library →
./references/third-party.md. For package authors only (SDK 57+): optional peer dependency, config declaration merging,Observe.registerIntegration(), and event naming.
Quick links to the docs
Get started: https://docs.expo.dev/eas/observe/get-started/
Dashboard guide: https://docs.expo.dev/eas/observe/dashboard/
Querying with EAS CLI: https://docs.expo.dev/eas/observe/eas-cli/
Querying with the Expo MCP Server: https://docs.expo.dev/eas/observe/mcp/
Metrics reference: https://docs.expo.dev/eas/observe/reference/metrics/
Expo Router integration: https://docs.expo.dev/eas/observe/integrations/expo-router/
React Navigation integration: https://docs.expo.dev/eas/observe/integrations/react-navigation/
Expo Image integration: https://docs.expo.dev/eas/observe/integrations/expo-image/
Reanimated integration: https://docs.expo.dev/eas/observe/integrations/react-native-reanimated/
User-defined events: https://docs.expo.dev/eas/observe/events/
Error reporting: https://docs.expo.dev/eas/observe/errors/
Configuration: https://docs.expo.dev/eas/observe/configuration/
Third-party integrations: https://docs.expo.dev/eas/observe/integrations/third-party/
EAS Update download performance: https://docs.expo.dev/eas/observe/eas-update/
Troubleshooting: https://docs.expo.dev/eas/observe/reference/troubleshooting/
Submitting Feedback
If you encounter errors, misleading or outdated information in this skill, report it so Expo can improve:
npx --yes @expo/agent-cli@latest feedback --category skills --subject "eas-observe" "<actionable feedback>"Only submit when you have something specific and actionable to report. Include as much relevant context as possible.
If an agent repeatedly fails at an Expo task or the user has to step in, submit a brief report of the task, expected result, and what went wrong using --category evals and the relevant Expo package or command as --subject.
Files
- ATTRIBUTION.mdAdded by AutoGineer809 B
- LICENSELicence file added by AutoGineer1.1 KiB
- SKILL.md5.7 KiB
- agents/openai.yaml449 B
- references/library-integrations.md6.8 KiB
- references/metrics.md8.5 KiB
- references/queries.md22.5 KiB
- references/setup.md27.4 KiB
- references/third-party.md4.6 KiB
references/metrics.md
EAS Observe metrics — interpretation cheatsheet
Quick reference for reading EAS Observe dashboards and CLI output.
Source: https://docs.expo.dev/eas/observe/reference/metrics/ — this is the canonical reference for metrics. Consult this page for the latest guidance, full prose definitions, optimization tips, and rationale.
All durations are in seconds. Metric data is retained for a minimum of 60 days. By default, every installation dispatches all of its events; high-volume apps can sample per installation with configure({ sampleRate }) — see Sampling.
Target thresholds
| Metric | Full name | Target | Auto-collected? |
|---|---|---|---|
| Cold launch | expo.app_startup.cold_launch_time | < 1.5s | Yes (native-only — JS code does not affect it) |
| Warm launch | expo.app_startup.warm_launch_time | < 0.5s | Yes (OS decides when warm vs cold happens) |
| Bundle load | expo.app_startup.bundle_load_time | < 0.3s | Yes (JS load + evaluation, before runApplication) |
| Time to first render (TTR) | expo.app_startup.ttr | < 2s incl. cold launch | Yes when root is wrapped with AppMetricsRoot (SDK 55) / ObserveRoot (SDK 56+) |
| Time to interactive (TTI) | expo.app_startup.tti | < 3s incl. cold launch | No — call markInteractive() once the screen is genuinely usable |
Both TTR and TTI are measured from native launch through the React render, so the cold-launch portion counts against them.
Interpreting TTI events (automatic params)
Every TTI event carries automatic params in three groups: frame rate, device state, and network state. Read the frame-rate group to classify what kind of slowness you're seeing, then read the device and network groups to decide whether the cause is the code or the conditions.
Frame rate — what kind of slowness
| Param | Definition | What it indicates |
|---|---|---|
expo.frameRate.slowFrames | Count of frames ≥ 17ms | Main thread consistently busy during launch (heavy layout, sync bridge calls, too many components rendering) |
expo.frameRate.frozenFrames | Count of frames ≥ 700ms | Hard freezes. Even one during startup is a serious issue (sync I/O, large JSON parsing, blocking network) |
expo.frameRate.totalDelay | Total accumulated time (seconds) frames exceeded their target duration | Best single "smoothness" number — compare to TTI |
Diagnostic patterns:
High TTI + low totalDelay → slow but smooth. The launch sequence itself is long. Optimize bundle size, data-fetch waterfalls, initialization chains.
High TTI + high totalDelay + many slowFrames → main-thread contention. Offload work, simplify the initial render tree.
High TTI + high totalDelay + any frozenFrames → something is blocking hard. Look for synchronous I/O, large JSON parsing, or blocking network calls.
Device state — is the regression environmental?
| Param | Type | What it indicates |
|---|---|---|
expo.device.lowPowerMode | boolean | OS power saver was active (Low Power Mode on iOS, Battery Saver on Android). It throttles CPU, GPU, and background work. A regression that disappears when you filter this out is environmental, not a code change. |
expo.device.thermalState | nominal | fair | serious | critical | unknown | Sustained serious/critical means the OS is throttling. Startup slows independently of any app change. |
expo.device.batteryLevel | number, 0–1 | Fractional charge at TTI. Rules out throttling on devices that manage performance aggressively at low charge. Omitted when the OS reports no value. |
expo.device.batteryCharging | boolean | Charging raises sustained CPU ceilings on iOS and some Android OEMs. Non-charging samples are the more conservative population. |
Network state — is startup network-bound?
| Param | Type | What it indicates |
|---|---|---|
expo.network.connected | boolean | If TTI degrades only when true, startup is network-bound. If it degrades when false, the app does too much before showing cached content. |
expo.network.type | wifi | cellular | ethernet | none | other | unknown | Compare cellular against Wi-Fi. A large gap points to network-bound startup work. VPN traffic reports the underlying transport. The value set is identical on both platforms, so dashboards need no per-platform branching. |
expo.network.isExpensive | boolean | Both platforms. The OS considers the connection metered (cellular, hotspot). Present only when a network exists. |
expo.network.isConstrained | boolean | iOS only. Low Data Mode is on for this path, so the system defers background transfers. |
expo.network.dataSaverEnabled | boolean | Android only. Data Saver is on. It is the nearest equivalent of Low Data Mode, but process-wide rather than per-path, hence the separate key. |
Network requests — was the network the cause?
TTI events summarize the HTTP requests made during launch, from the end of the native launch to the markInteractive() call. Traffic is observed automatically — URLSession on iOS, OkHttpClient on Android, which covers fetch — and Observe's own uploads are excluded. All of these are omitted when the window held no requests.
| Param | Unit | What it indicates |
|---|---|---|
expo.network.requests.count | count | Requests that finished in the window. A request still in flight when the app became interactive is not counted anywhere in this table. |
expo.network.requests.failed | count | Errored, returned 4xx/5xx, never got a response, or broke partway through the body. Redirects are not failures. |
expo.network.requests.bytesReceived / .bytesSent | bytes | On-the-wire totals for the window. |
expo.network.requests.totalDuration | seconds | Sum of every request duration, failures included. Exceeds wall-clock when requests overlap; one timeout contributes the client's full timeout interval. |
expo.network.requests.throughputBytesPerSecond | bytes/sec | Received bytes over the time bytes were actually moving (union of transfer windows, measured from each first byte). Excludes DNS, connect, server think time, cache hits, and failures. Requests the OS did not clearly identify as network loads are excluded too. Omitted when nothing was received. |
expo.network.requests.slowest.duration | seconds | The single longest completed request. Requests that never produced a response are excluded, since a timeout measures the client's own setting. |
expo.network.requests.slowest.host | string | Host of that request. |
expo.network.requests.slowest.statusCode | number | Explains an empty response: bytesReceived of 0 is routine on a 304, a problem on a 200. |
expo.network.requests.slowest.timeToFirstByte | seconds | Includes server processing, so treat it as a proxy for network quality, not a measurement of it. |
expo.network.requests.slowest.bytesReceived | bytes | Separates "slow because it moved a lot of data" from "slow while idle". |
Diagnostic patterns:
slowest.durationmostlytimeToFirstByte→ the server was slow to answer. Optimize the endpoint, or stop blocking startup on it.Small
timeToFirstByte+ largebytesReceived→ the transfer itself was slow. Shrink the payload or defer it.High
failed+ hightotalDuration→ the launch burned time on requests that never arrived. Add timeouts and render cached content first.Low
throughputBytesPerSecondonwifi→ suspect the population, not the code; cross-checkisExpensiveandisConstrained/dataSaverEnabled.
The summary is bounded by an in-memory ring buffer of the 200 most recent requests. A launch that makes more undercounts, so read these as a sample of a very busy window.
Custom params
You can attach your own params to the TTI event, and override the route name it is tagged with. See ./setup.md for the call syntax.
Dispatch caveats
Debug builds (native debug OR JS bundle with
__DEV__= true) do not dispatch metrics unlessconfigure({ dispatchInDebug: true })is set.The
environmenttag (defaults toprocess.env.NODE_ENV) is metadata only — it does not gate dispatch by itself.Offline events are buffered on-device and flushed when the app backgrounds or
Observe.dispatchEvents()is called.
Cross-references
Full metric definitions and optimization guidance: https://docs.expo.dev/eas/observe/reference/metrics/
Setup steps (
AppMetricsRoot/ObserveRoot,markInteractive): see./setup.md.Querying metrics via the EAS CLI: see
./queries.md.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs an MCP server, Needs Node.js
Security
What our scanner noticed· Low risk
- Instructions that use the network
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Expo
- Publisher
- Expo (Official)
- Source
- expo/skills · plugins/expo/skills/eas-observe · commit a9e5bd3
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from plugins/expo/LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Expo or Expo.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download eas-observe.zip