Generated banner for Eas Observe
Generated logo for Eas Observe

Eas Observe

ExpoOfficialPublished by Expo

  • Skill
  • Tool integration
  • Software development
  • MIT
  • Low risk

From the author

Use for anything related to EAS Observe - adding `expo-observe` to an Expo project (AppMetricsRoot/ObserveRoot HOC, markInteractive and ObserveInteractiveMarker, the useObserve hook, the Expo Router / React Navigation integrations for per-route metrics, the expo-image and Reanimated integrations, user-defined events via `Observe.logEvent`, error reporting via ObserveErrorBoundary and `Observe.reportError`, native crash reporting, source map upload for symbolicated stack traces, and runtime config such as sampleRate and dispatchInDebug), querying via the EAS CLI (`eas observe:metrics-summary`, `observe:metrics`, `observe:routes`, `observe:events`, `observe:session`, `observe:versions`, `observe:errors`, `observe:event`) or the Expo MCP `observe_*` tools, interpreting the resulting metrics (cold/warm launch, TTR, TTI, navigation cold/warm TTR, update download, and the TTI frameRate/device/network params for triaging slow startups), or shipping an Observe integration inside a third-party package.
— the description by Expo

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

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/expo/eas-observe/download
  2. Unzip 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 (AppMetricsRoot on SDK 55, ObserveRoot on SDK 56+), mark the app interactive (global markInteractive() on SDK 55, the useObserve() hook or <ObserveInteractiveMarker /> on SDK 56+), optional per-route navigation metrics through the Expo Router / React Navigation integrations, user-defined events via Observe.logEvent (SDK 56+), error reporting, and runtime configuration (sampling, dispatch, environments, custom endpoint).

  • Querying metrics from the terminal → ./references/queries.md. The eas observe:* commands — metrics-summary, metrics, routes, events, session, versions, errors, event — with flags, metric aliases, table layouts, JSON shapes, and common workflows. Also the matching observe_* 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.oversized events for images decoded far larger than the screen) and react-native-reanimated (reanimated.error errors and optional reanimated.warning events).

  • 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.

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

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

MetricFull nameTargetAuto-collected?
Cold launchexpo.app_startup.cold_launch_time< 1.5sYes (native-only — JS code does not affect it)
Warm launchexpo.app_startup.warm_launch_time< 0.5sYes (OS decides when warm vs cold happens)
Bundle loadexpo.app_startup.bundle_load_time< 0.3sYes (JS load + evaluation, before runApplication)
Time to first render (TTR)expo.app_startup.ttr< 2s incl. cold launchYes when root is wrapped with AppMetricsRoot (SDK 55) / ObserveRoot (SDK 56+)
Time to interactive (TTI)expo.app_startup.tti< 3s incl. cold launchNo — 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
ParamDefinitionWhat it indicates
expo.frameRate.slowFramesCount of frames ≥ 17msMain thread consistently busy during launch (heavy layout, sync bridge calls, too many components rendering)
expo.frameRate.frozenFramesCount of frames ≥ 700msHard freezes. Even one during startup is a serious issue (sync I/O, large JSON parsing, blocking network)
expo.frameRate.totalDelayTotal accumulated time (seconds) frames exceeded their target durationBest 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?
ParamTypeWhat it indicates
expo.device.lowPowerModebooleanOS 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.thermalStatenominal | fair | serious | critical | unknownSustained serious/critical means the OS is throttling. Startup slows independently of any app change.
expo.device.batteryLevelnumber, 0–1Fractional charge at TTI. Rules out throttling on devices that manage performance aggressively at low charge. Omitted when the OS reports no value.
expo.device.batteryChargingbooleanCharging raises sustained CPU ceilings on iOS and some Android OEMs. Non-charging samples are the more conservative population.
Network state — is startup network-bound?
ParamTypeWhat it indicates
expo.network.connectedbooleanIf 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.typewifi | cellular | ethernet | none | other | unknownCompare 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.isExpensivebooleanBoth platforms. The OS considers the connection metered (cellular, hotspot). Present only when a network exists.
expo.network.isConstrainedbooleaniOS only. Low Data Mode is on for this path, so the system defers background transfers.
expo.network.dataSaverEnabledbooleanAndroid 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.

ParamUnitWhat it indicates
expo.network.requests.countcountRequests 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.failedcountErrored, returned 4xx/5xx, never got a response, or broke partway through the body. Redirects are not failures.
expo.network.requests.bytesReceived / .bytesSentbytesOn-the-wire totals for the window.
expo.network.requests.totalDurationsecondsSum of every request duration, failures included. Exceeds wall-clock when requests overlap; one timeout contributes the client's full timeout interval.
expo.network.requests.throughputBytesPerSecondbytes/secReceived 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.durationsecondsThe 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.hoststringHost of that request.
expo.network.requests.slowest.statusCodenumberExplains an empty response: bytesReceived of 0 is routine on a 304, a problem on a 200.
expo.network.requests.slowest.timeToFirstBytesecondsIncludes server processing, so treat it as a proxy for network quality, not a measurement of it.
expo.network.requests.slowest.bytesReceivedbytesSeparates "slow because it moved a lot of data" from "slow while idle".

Diagnostic patterns:

  • slowest.duration mostly timeToFirstByte → the server was slow to answer. Optimize the endpoint, or stop blocking startup on it.

  • Small timeToFirstByte + large bytesReceived → the transfer itself was slow. Shrink the payload or defer it.

  • High failed + high totalDuration → the launch burned time on requests that never arrived. Add timeouts and render cached content first.

  • Low throughputBytesPerSecond on wifi → suspect the population, not the code; cross-check isExpensive and isConstrained / 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 unless configure({ dispatchInDebug: true }) is set.

  • The environment tag (defaults to process.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

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.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download eas-observe.zip