ReadyNative

Amplitude

Set up Amplitude for analytics in an Expo app with ReadyNative: product analytics · Expo Go OK (JS SDK, storage adapter)

Pro

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
  1. Open app.amplitude.com → [Settings] → [Projects] → your project and copy the API key into .env as EXPO_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".
  2. If your data has to stay in the EU, add serverZone: "EU" to the init options in src/lib/analytics.ts.
  3. Restart the dev server with bun run start -- -c so the key is picked up, then open [Analytics] → [User Look-Up] in Amplitude and watch for [Amplitude] Session Start.
  4. 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; disableCookies is on and migrateLegacyData is off.
  5. For the full device context (Application Installed/Updated events, idfv, app-set-id), make a dev build with npx expo run:ios or npx 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.

  1. Run bun run doctor - every row for this module should be green.

The keys, in one place:

KeyWhere to get it
EXPO_PUBLIC_AMPLITUDE_KEYhttps://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");

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 none removes everything; with --with-examples, /examples/analytics then shows "Module not installed".
  • Expo Go works, but without the optional native module there are no Application Installed/Updated events and no idfv or 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):

  1. Put a real EXPO_PUBLIC_AMPLITUDE_KEY in .env, run bun run start -- -c, and open the app on a device (Expo Go is fine).
  2. Amplitude → Analytics → User Look-Up → search by device, or wait for [Amplitude] Session Start: a user appears within about a minute.
  3. Examples → "Analytics event" → tap Track event → example_pressed with source=examples/analytics shows in that user's event stream. The SDK flushes every second or 30 events.
  4. Tap Identify user: the user gets user_id=demo-user and the user property plan=free.
  5. Navigate between tabs and watch one [Amplitude] Screen Viewed arrive per pathname.
  6. 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-modules

In a finalized tree setup is a stub, so you undo it by hand. Here is everything this module added:

  1. 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.
  2. Replace, don't delete src/lib/analytics.ts, src/lib/tracking.ts: core code imports them, so swap in the no-op version from modules/analytics/none/files/ of a fresh clone of your tier repo - same exports, nothing behind them.
  3. Uninstall the dependencies: bun remove @amplitude/analytics-react-native expo-tracking-transparency.
  4. Drop the config plugin expo-tracking-transparency from .readynative.json → modules.app.expo.plugins (that is where app.config.ts reads it from), then rebuild the dev build.
  5. Unwrap the provider: delete <AnalyticsProvider> and its import from src/providers.tsx.
  6. Remove the env keys EXPO_PUBLIC_AMPLITUDE_KEY from .env, .env.example and your EAS environment, and EXPO_PUBLIC_AMPLITUDE_KEY from src/lib/env.ts.
  7. Update the privacy declarations: remove this module's entries from .readynative.json → modules.app.expo.ios.privacyManifests, then re-run bun run gen:privacy and revise your store privacy answers.
  8. Check it: bun run typecheck and bun run lint point at anything that still imports the removed files; bun run gen:graph refreshes docs/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 amplitude

Module id: analytics/amplitude.

Dependencies

PackageVersionKind
@amplitude/analytics-react-native^1.8.0dependency
expo-tracking-transparency~57.0.2dependency (expo install)

Config plugins

Merged into app.config.ts through .readynative.json (modules.app):

  • expo-tracking-transparency (with options)

Environment keys

KeyRequiredServer-onlyExampleDocs
EXPO_PUBLIC_AMPLITUDE_KEYyesno0123456789abcdef0123456789abcdefdashboard

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):

TypeLinked to userTrackingPurposes
ProductInteractionyesnoAnalytics
DeviceIDyesnoAnalytics
UserIDyesnoAnalytics
CoarseLocationyesnoAnalytics

Providers

Rendered in src/providers.tsx (lower order = outermost):

OrderProviderFrom
40AnalyticsProvider@/lib/analytics

Compatibility

Doctor checks

  • env: EXPO_PUBLIC_AMPLITUDE_KEY

After setup

  1. Amplitude: create a project at https://app.amplitude.com (Settings > Projects), copy its API key into EXPO_PUBLIC_AMPLITUDE_KEY.
  2. 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.
  3. 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.
  4. 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.tracking is 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 and analytics.identify waits 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.tsx
  • src/lib/__tests__/analytics-amplitude.test.ts
  • src/lib/__tests__/tracking.test.ts
  • src/lib/analytics.ts
  • src/lib/tracking.ts
  • src/screens/examples/analytics-example-screen.tsx

On this page

Get ReadyNative