Paying for two monitoring services had been nagging at me for a while. So when EAS Observe moved from public beta to general availability on August 20, 2026, my first thought was that I could finally drop down to one.
I could not. EAS Observe does not capture native crashes, and the documentation says so plainly.
The time spent looking was still worth it, because it surfaced numbers I had never been able to see. What follows is what I confirmed by reading the GA announcement and the docs side by side, reordered into the sequence you would actually hit when adding it to an app.
The first question: can this replace your crash reporter?
EAS Observe measures how your app performs in production — cold launch time, warm launch time, Time to First Render, Time to Interactive, bundle load time, and EAS Update download time.
None of those are measurable from your own machine. Your development device is fast and your connection is stable. Your actual users may be on a three-year-old Android phone with poor reception. Once you frame the service as a way to close that gap, its position becomes clear.
Two things sit outside that scope:
| Area | Current status |
|---|---|
| Native crashes | Not supported. You still need a crash reporting service such as Sentry or BugSnag alongside it |
| JavaScript errors | Available in preview on SDK 57 and later, with symbolicated stack traces in the dashboard |
I ship several apps on my own, and a few of them have been public long enough that the device spread is wide. In my experience the painful failures are the ones that never reach the JavaScript layer at all. When something dies inside a native library, your JS error handler never runs. If that class of failure is not covered, the existing crash reporter has to stay.
The framing in the docs is honest, though. It says the capability is not there yet and tells you to use something else until it is. That is far more useful than a vague claim.
How far does 100,000 free events actually go?
General availability also locked in the pricing tiers.
| Plan | Events per month | Dashboards |
|---|---|---|
| Free | 100,000 | Main Observe dashboard only |
| Starter and above | 500,000 | Current and future observability dashboards |
Past the included allowance, Starter plans and above are billed at $5 per 1 million events.
Raw event counts are hard to reason about, so it helps that Expo publishes a rough translation: 100,000 events corresponds to roughly 10,000 monthly active users, and 500,000 to roughly 50,000. If you have just shipped your first app, the free tier has plenty of room.
My own plan is to roll it out starting from the apps with the fewest users. Testing on your busiest app means you are evaluating the quota and validating the implementation at the same time, and both judgments get sloppy.
Retention deserves a closer look. The GA announcement states 90 days on every plan. The FAQ in the documentation says metric data is retained for a minimum of 60 days. Both are official, and you could reasonably read the announcement as the newer figure — but I am planning around 60 days as the floor. If you want to compare across a quarter boundary, that gap matters.
Three steps to install, but not in Expo Go
You need an Expo account, SDK 55 or later, and a linked EAS project. If extra.eas.projectId is missing from your app config, run eas init to create one.
Here is the first real obstacle: EAS Observe depends on the expo-observe native library, so it does not run in Expo Go. You need a development build or a production build. If you have only ever exercised your Rork app through preview, this is the actual entry point.
Installation is two lines:
npx expo install --fix
npx expo install expo-observeThen wrap your root layout. Watch the component name, because it changed with the SDK: SDK 55 uses AppMetricsRoot, SDK 56 and later use ObserveRoot.
import { Stack } from 'expo-router';
import { ObserveRoot } from 'expo-observe';
function RootLayout() {
return <Stack />;
}
export default ObserveRoot.wrap(RootLayout);Time to First Render is measured automatically from that point on. Getting your first metric out of a single wrapper call keeps the cost of trying it low, which I appreciate as a design decision.
If you are not sure which SDK version you are on, npx expo-doctor will tell you — though it has blind spots of its own, which I wrote up in a walkthrough of what its 22 checks actually cover.
Calling markInteractive on only one screen leaves gaps
The third step is calling markInteractive(). You call it once everything behind the splash screen has finished — update checks, authentication, initial data fetching, splash animation — and the app is genuinely ready for input.
useEffect(() => {
if (isReady) {
SplashScreen.hide();
markInteractive();
}
}, [isReady, markInteractive]);There is a trap tucked into a docs note here.
markInteractive() is safe to call multiple times in a session, but only the first call is recorded. And if your app has more than one entry screen — onboarding, login, deep link destinations — you have to call it on every one of them. Otherwise Time to Interactive is simply not recorded for sessions that started there.
This matters more than it sounds. If you route users from a home screen widget or a push notification straight into a detail screen, the numbers for exactly that path go missing, quietly. A dashboard full of data looks complete, while the path that most shapes the experience is absent from it.
One of my wallpaper apps opens a detail screen directly from a notification, so I was glad to have read that note rather than skimmed past it.
One more thing: metrics from debug builds are not dispatched by default. To verify your integration during development, set dispatchInDebug to true via configure(). "I wired it up and nothing shows up" is almost always this.
Levers to pull when event volume climbs
There is information in the GA announcement that does not appear in the setup guide: what to do when your event volume runs past what you expected.
The first lever is the sampling rate. Instead of sending every session, you send a fixed proportion. The more users you have, the less you need all of them for the statistics to hold.
The second is turning collection off entirely, from the "Observe data ingestion" section of your project settings. When a service bills by usage, adding it without knowing how to stop it is uncomfortable. Find the off switch before you turn it on.
If you prefer the terminal, eas observe:metrics-summary gives you median, p75, and p95 values grouped by app version. Use eas observe:session to walk through a single slow session, and eas observe:routes for per-route timings. Not having to open a dashboard makes a post-release check much easier to turn into a habit.
Where to start
Pick one of your lower-traffic apps, install expo-observe, and cut a development build. Getting numbers out of it takes one wrapper and one call to markInteractive(). That is the point where you find out what your app's cold start actually costs in seconds.
And leave your existing crash reporter in place. For now this is addition, not replacement.
If you would rather begin by pinning down which Expo version is actually in your app, the walkthrough on what package-lock.json really resolves to covers that.