RORK LABJP
PLAY — Google Play's target API level 36 requirement took effect yesterday, August 31. From today, new apps and updates must target Android 16VISIBILITY — Apps still on API 35 stay listed but disappear for users on newer Android versions. No error is raised; new installs simply fade, which makes the change easy to missEXTENSION — If you missed the deadline, an extension through November 1, 2026 can be requested in Play Console — best filed alongside a concrete migration planAPPLE — On the Apple side, the event lands September 9 and iOS 27 is reported to ship September 14. Testing generated apps on iOS 27 hardware before release week is time well spentEXPO — Expo released expo-paste-input on August 28, a native module that brings image, GIF, and sticker paste to React Native TextInputEAS — EAS Observe reached general availability on August 20, putting crash and performance monitoring on the same EAS platform as builds and updatesPLAY — Google Play's target API level 36 requirement took effect yesterday, August 31. From today, new apps and updates must target Android 16VISIBILITY — Apps still on API 35 stay listed but disappear for users on newer Android versions. No error is raised; new installs simply fade, which makes the change easy to missEXTENSION — If you missed the deadline, an extension through November 1, 2026 can be requested in Play Console — best filed alongside a concrete migration planAPPLE — On the Apple side, the event lands September 9 and iOS 27 is reported to ship September 14. Testing generated apps on iOS 27 hardware before release week is time well spentEXPO — Expo released expo-paste-input on August 28, a native module that brings image, GIF, and sticker paste to React Native TextInputEAS — EAS Observe reached general availability on August 20, putting crash and performance monitoring on the same EAS platform as builds and updates
Articles/Getting Started
Getting Started/2026-09-01Beginner

Starting with the EAS Observe free tier, and why Crashlytics stays installed

EAS Observe reached general availability on August 20. I looked into consolidating down to one monitoring service and found that native crashes are still out of scope. Here are the free-tier limits and the setup details that trip people up.

Expo194EAS ObservePerformance monitoringRork548Indie development2

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:

AreaCurrent status
Native crashesNot supported. You still need a crash reporting service such as Sentry or BugSnag alongside it
JavaScript errorsAvailable 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.

PlanEvents per monthDashboards
Free100,000Main Observe dashboard only
Starter and above500,000Current 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-observe

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

References

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Getting Started2026-08-29
Nine Expo patches shipped in thirty days. Which one is your app actually running?
A tilde in package.json does not mean you are on the newest patch. The file that decides what actually gets installed is package-lock.json. Here is what npm did when I ran the checks myself.
Getting Started2026-08-28
Sunset.png and sunset.png: one file on your Mac, two on the build server
An image that renders locally but breaks the cloud build. The cause was filename casing. Here is the reproduction, the git behavior behind it, and a small checker with measured results.
Getting Started2026-08-22
The names you can change, and the ones you can't — the 30 minutes before your first Rork release
An app has three names, and only the display name can be changed after release. Here is how I check the identifiers Rork fills in by default, using a dependency-free script, before I hit submit.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →