Amplitude
Set up Amplitude for analytics in an Expo app with ReadyNative: product analytics · Expo Go OK (JS SDK, storage adapter)
Expo Go: yes
Runs in Expo Go; no dev build needed for this module.
This sends product analytics to Amplitude: src/lib/analytics.ts initialises @amplitude/analytics-react-native 1.x on the first analytics consent (only when the key is set) and implements analytics.track/identify/screen, with a pass-through AnalyticsProvider (order 40, since the SDK has no React provider). screen maps to trackScreenView ([Amplitude] Screen Viewed) and identify maps to setUserId plus an Identify call for user properties, where null unsets a property. If you set up with --with-examples there's an example at /examples/analytics with a list of the last events sent. @/lib/analytics keeps the same API whichever option you pick - pick Amplitude when your team already lives in its funnels and cohorts.
Setup
bun run setup --analytics amplitude- Open app.amplitude.com → [Settings] → [Projects] → your project and copy the API key into
.envasEXPO_PUBLIC_AMPLITUDE_KEY. It's publishable and safe in the client. Without it every call is a no-op and the example shows "Configure EXPO_PUBLIC_AMPLITUDE_KEY". - If your data has to stay in the EU, add
serverZone: "EU"to theinitoptions insrc/lib/analytics.ts. - Restart the dev server with
bun run start -- -cso the key is picked up, then open [Analytics] → [User Look-Up] in Amplitude and watch for[Amplitude] Session Start. - Persistence - session and the event queue - goes through the storage adapter (
createAdapterStorage()on@/lib/storage), so the SDK's default async-storage is never loaded;disableCookiesis on andmigrateLegacyDatais off. - For the full device context (
Application Installed/Updatedevents,idfv, app-set-id), make a dev build withnpx expo run:iosornpx expo run:android. Expo Go still works - the SDK's native module is optional, and device and OS fields then come from the JS user agent.
Going to production?
Use a separate Amplitude project for production so your own test events don't skew the charts, and swap the key per build profile (eas.json env). The key is public, so there's nothing to keep secret - don't point release builds at your dev project.
- Run
bun run doctor- every row for this module should be green.
The keys, in one place:
| Key | Where to get it |
|---|---|
EXPO_PUBLIC_AMPLITUDE_KEY | https://app.amplitude.com/analytics/settings/projects → your project → API key. Publishable. |
Dashboards: the events stream at app.amplitude.com → Data → Events (or Analytics → User Look-Up for one user) · project settings and API key. Deps: @amplitude/analytics-react-native ^1.8, which pulls @amplitude/analytics-core (async-storage comes along as a transitive dep but is unused).
Usage
Track an event and identify the user:
import { analytics } from "@/lib/analytics";
analytics.track("checkout_started", { plan: "pro" });
analytics.identify(user.id, { email: user.email });Reset on sign-out, which gives you a new device id and an anonymous user:
import { analytics } from "@/lib/analytics";
analytics.reset();Screens are already tracked for you by the root layout, but you can send one by hand:
import { analytics } from "@/lib/analytics";
analytics.screen("/checkout");Consent
init runs only on the first "yes" to analytics consent, with optOut: false and trackingOptions: { ipAddress: false, adid: false, carrier: false }; before that nothing is initialised or sent. Withdrawing consent calls setOptOut(true), consent again setOptOut(false). With consent/consent that means nothing runs in the EU/EEA, UK, CH, CA, BR until the sheet is answered, it runs elsewhere, and the Settings → Privacy toggle flips it live. isAnalyticsConfigured only means the key is set; isAnalyticsActive() tells you whether the SDK is running. analytics.reset() sets the user id to undefined and issues a new device id, leaving optOut alone - the root layout's useSignOutCleanup calls it through wipeLocalData() on every sign-out, and on account deletion.
Privacy declarations: UserID and CoarseLocation are declared linked: true - identify links them to the user, and Amplitude derives a coarse location server-side.
Feature flags
analytics.isFeatureEnabled / getFeatureFlag / useFeatureFlag exist for parity with PostHog but always return undefined here: Amplitude Analytics has no flags, Amplitude Experiment is a separate SDK (@amplitude/experiment-react-native-client). Gate on === true so code stays correct across providers.
Tracking (ATT)
The module installs expo-tracking-transparency with a purpose string in app.config.ts (NSUserTrackingUsageDescription) and ships src/lib/tracking.ts - the same tracking.getStatus() / requestPermission() + useTrackingStatus() surface as the core shim. First-party product analytics is not tracking, so features.tracking defaults to false in readynative.config.ts: nothing ever prompts and you answer "No" to tracking in App Store Connect. Flip it to true when you link data across companies (ads attribution, IDFA): AnalyticsProvider then asks once the app is in the foreground (iOS answers denied silently if asked from the background) and analytics.identify waits for granted. To pitch the value first, remove that call from the provider and call tracking.requestPermission() behind your own explainer screen. app.config.ts adds the expo-tracking-transparency plugin only when features.tracking is true. With it false the plugin is dropped, com.google.android.gms.permission.AD_ID goes into android.blockedPermissions, and the privacy manifest gets NSPrivacyTracking: false; the NSUserTrackingUsageDescription string stays, because the library is still linked and App Store Connect flags a linked ATT framework without one (ITMS-90683). doctor --store reports the flag/library state either way.
Gotchas
- Storage
reset()is a no-op, because the adapter can't enumerate keys. Only Amplitude's disabled legacy migration calls it. bun run setup --analytics noneremoves everything; with--with-examples,/examples/analyticsthen shows "Module not installed".- Expo Go works, but without the optional native module there are no
Application Installed/Updatedevents and noidfvor app-set-id. Make a dev build for the full context.
Check it works (the Examples steps need a tree set up with --with-examples):
- Put a real
EXPO_PUBLIC_AMPLITUDE_KEYin.env, runbun run start -- -c, and open the app on a device (Expo Go is fine). - Amplitude → Analytics → User Look-Up → search by device, or wait for
[Amplitude] Session Start: a user appears within about a minute. - Examples → "Analytics event" → tap Track event →
example_pressedwithsource=examples/analyticsshows in that user's event stream. The SDK flushes every second or 30 events. - Tap Identify user: the user gets
user_id=demo-userand the user propertyplan=free. - Navigate between tabs and watch one
[Amplitude] Screen Viewedarrive per pathname. - Remove the key and restart: the example shows "Configure EXPO_PUBLIC_AMPLITUDE_KEY" and the app still boots.
Remove it
While modules/ exists (a tree set up with --keep-modules), setup does all of it:
bun run setup --analytics none --yes --keep-modulesIn a finalized tree setup is a stub, so you undo it by hand. Here is everything this module added:
- Delete the files that are still there (the demo screens are gone already unless you set up with
--with-examples):src/app/examples/analytics.tsx,src/lib/__tests__/analytics-amplitude.test.ts,src/lib/__tests__/tracking.test.ts,src/screens/examples/analytics-example-screen.tsx. - Replace, don't delete
src/lib/analytics.ts,src/lib/tracking.ts: core code imports them, so swap in the no-op version frommodules/analytics/none/files/of a fresh clone of your tier repo - same exports, nothing behind them. - Uninstall the dependencies:
bun remove @amplitude/analytics-react-native expo-tracking-transparency. - Drop the config plugin
expo-tracking-transparencyfrom.readynative.json→modules.app.expo.plugins(that is whereapp.config.tsreads it from), then rebuild the dev build. - Unwrap the provider: delete
<AnalyticsProvider>and its import fromsrc/providers.tsx. - Remove the env keys
EXPO_PUBLIC_AMPLITUDE_KEYfrom.env,.env.exampleand your EAS environment, andEXPO_PUBLIC_AMPLITUDE_KEYfromsrc/lib/env.ts. - Update the privacy declarations: remove this module's entries from
.readynative.json→modules.app.expo.ios.privacyManifests, then re-runbun run gen:privacyand revise your store privacy answers. - Check it:
bun run typecheckandbun run lintpoint at anything that still imports the removed files;bun run gen:graphrefreshesdocs/ARCHITECTURE.md.
Reference
Everything below is generated from modules/analytics/amplitude/module.json - the same file bun run setup reads, so it is what actually lands in your repo.
Install
bun run setup --analytics amplitudeModule id: analytics/amplitude.
Dependencies
| Package | Version | Kind |
|---|---|---|
@amplitude/analytics-react-native | ^1.8.0 | dependency |
expo-tracking-transparency | ~57.0.2 | dependency (expo install) |
Config plugins
Merged into app.config.ts through .readynative.json (modules.app):
expo-tracking-transparency(with options)
Environment keys
| Key | Required | Server-only | Example | Docs |
|---|---|---|---|---|
EXPO_PUBLIC_AMPLITUDE_KEY | yes | no | 0123456789abcdef0123456789abcdef | dashboard |
Keys go in .env (see .env.example). Required keys are checked by bun run doctor; Server-only keys have no EXPO_PUBLIC_ prefix, are read only by API routes and never reach the bundle.
Privacy
Play Data safety draft: collects App interactions (events, screens), Device or other ids (device id), User IDs (when you call analytics.identify), Approximate location (derived from the request IP); shared with Amplitude (processor). Source of truth: vendor disclosure.
Apple privacy manifest data types (composed into ios.privacyManifests by setup):
| Type | Linked to user | Tracking | Purposes |
|---|---|---|---|
ProductInteraction | yes | no | Analytics |
DeviceID | yes | no | Analytics |
UserID | yes | no | Analytics |
CoarseLocation | yes | no | Analytics |
Providers
Rendered in src/providers.tsx (lower order = outermost):
| Order | Provider | From |
|---|---|---|
| 40 | AnalyticsProvider | @/lib/analytics |
Compatibility
- Requires
storage=kv-store,mmkv,async-storage
Doctor checks
- env:
EXPO_PUBLIC_AMPLITUDE_KEY
After setup
- Amplitude: create a project at https://app.amplitude.com (Settings > Projects), copy its API key into EXPO_PUBLIC_AMPLITUDE_KEY.
- Amplitude: open Data > Events (or User Look-Up), call
analytics.track("hello")from any screen (or open /examples/analytics with--with-examples) and watch it arrive. - Amplitude: install/update lifecycle events and idfv/app-set-id need the SDK's native module - make a dev build (
npx expo run:ios); Expo Go tracks everything else. - App Tracking Transparency: expo-tracking-transparency is installed, but app.config.ts only adds its plugin, the NSUserTrackingUsageDescription purpose string and NSPrivacyTracking: true when
features.trackingis true in readynative.config.ts - set it only if you link data across companies (ads attribution, IDFA); the prompt then runs once the app is active andanalytics.identifywaits for it. With it off (the default) Android's AD_ID permission is blocked and you answer 'No' to tracking in App Store Connect.
Files
6 files copied to the project root
src/app/examples/analytics.tsxsrc/lib/__tests__/analytics-amplitude.test.tssrc/lib/__tests__/tracking.test.tssrc/lib/analytics.tssrc/lib/tracking.tssrc/screens/examples/analytics-example-screen.tsx
PostHog
Set up PostHog for analytics in an Expo app with ReadyNative: product analytics + feature flags (analytics.isFeatureEnabled / useFeatureFlag) · Expo Go OK
No analytics
The "No analytics" option of the ReadyNative Expo template: analytics.track/identify/screen are no-ops, feature flags read undefined.