You know the drill. Sign up for PostHog, create an org, create a project, decide which of the four SDK packages you actually need this time, hook up the config plugin, copy the project key out of one browser tab and paste it into .env.local in another, then repeat that last step for every EAS environment so your team and your CI can see the same data you do. By the time analytics is actually capturing events, you've spent half an hour on plumbing.
That setup is now one command. It creates your PostHog org and project, installs the SDK, adds the config plugin, and writes your keys into .env.local and your EAS environment variables for Production, Preview, and Development. You pick the data region and the features you want, and it's done. If you already have a PostHog account, it opens your browser once to link the existing org and project, then runs the same setup against it.
That's the news. The more interesting part is what the integration does once it's installed: every PostHog event from your app now carries the release it came from, and your EAS Workflows can read PostHog metrics and decide what happens next.
What the integration actually sets up
Product analytics tells you what a user did. On its own, it doesn't tell you which build, channel, or update they were running when they did it. That's the gap this integration closes, and it does it with almost no setup on your part.
A few lines in your app register eas/update_id, eas/channel, and eas/runtime_version as super properties. Once that's in place, every capture() call carries them automatically. Your workflows speak the same language: the native posthog_capture_event function takes the same eas/ names, filled in from contexts like $${{ app.id }} or job outputs like build_id, so an event fired from CI looks exactly like one fired from a phone in someone's pocket.
PostHog recognizes these property names on its own. It shows the Expo logo next to them in the UI and turns the values into links back to your Expo dashboard. Click an eas/update_id inside a PostHog event and you land directly on that update, no copy-pasting IDs between tabs.
That changes the shape of a debugging session. A funnel drops on Tuesday: you break it down by update ID, find the update where it started, and open it, all from inside PostHog. A crash spike, a slow screen, a retention dip right after a channel rollout, it's the same three clicks every time instead of matching timestamps by hand.
Rollouts that watch their own metrics
This is where it stops being "analytics with better tags" and turns into something that changes what release day looks like.
An EAS Workflow is a chain of jobs, and PostHog now has native functions you can drop into that chain. posthog_flag_rollout sets a feature flag to a given percentage. posthog_wait_for_metric runs a HogQL query on an interval and holds the job open until the result clears your threshold, or fails it out on a timeout. posthog_wait_for_query is the simpler yes/no version: it clears the moment the query returns true. Put one of these next to your EAS Update or EAS Build job, and the workflow stops being fire-and-forget. It ships the change, watches production, and picks the next step based on what it sees.
Feature flags aren't the only lever here. The same pattern works for channel rollouts: publish an update to 10 percent of a channel, watch the error rate for a window, then either widen it to everyone or revert. The recipe also logs how close each rollout got to its threshold as a PostHog event, so after a handful of releases you end up with a chart of your own rollout history, for free.
A few things fall out of these primitives once you start combining them:
- A kill switch that flips a flag off the second errors spike, on a schedule, with nobody paged.
- A nightly error budget check that stays quiet until you've actually spent the budget.
- A submission job that drops an annotation on your PostHog timeline, so every deploy shows up on the charts your team already has open.
-
posthog_upload_sourcemapswired into the build job, so stack traces in PostHog's error tracking point at your TypeScript instead of a minified bundle.
How to set it up
Run eas integrations:posthog:connect in a project that's linked to EAS. Choose a region, choose the features you want enabled, and let it finish. Then wrap your app in <PostHogProvider>, add the release properties, and cut a development build. Fire one test event and check eas integrations:posthog:dashboard to confirm it landed in PostHog.
Limitations
Product analytics itself works fine inside Expo Go. Session replay and crash symbolication need a development build, and release tracking specifically only kicks in once your app is actually running a real update, since that's where the eas/ properties come from.
Where to go from here
- Start with Using PostHog for the full setup walkthrough.
- Browse the recipes for over 10 ready-made workflows you can drop into
.eas/workflows/. - Check the native function reference if you want to wire PostHog into a workflow you already have.
This post is based on content from the Expo blog. Follow @expo for more React Native content.
Top comments (0)