Expojet
Connect services

Mobile Analytics

Privacy-first and product analytics for Expo with PostHog and Aptabase.

Expojet provides production-ready, type-safe mobile analytics adapters for Expo apps. Choose between comprehensive product analytics with PostHog, lightweight privacy-first telemetry with Aptabase, or completely opt out with None.


Adapter Matrix

ProviderLibraryPrivacy ModelBest For
PostHogposthog-react-nativeConfigurable / Product AnalyticsAutocapture, funnels, cohort analysis, session replay, and feature flags
Aptabase@aptabase/react-nativePrivacy-First / Zero PIILightweight telemetry, crash rates, and privacy compliance (GDPR/CCPA)
None—Zero TelemetryOffline-first utilities or apps requiring zero external analytics

CLI Usage

Select an analytics adapter interactively during scaffolding or via flags:

# PostHog analytics
npx create-expojet@latest my-app --analytics posthog
# Or using the shortcut:
npx create-expojet@latest my-app --posthog

# Aptabase analytics
npx create-expojet@latest my-app --analytics aptabase
# Or using the shortcut:
npx create-expojet@latest my-app --aptabase

# Explicitly disable analytics (default)
npx create-expojet@latest my-app --analytics none

Architecture & Secret Boundary Isolation

Mobile application bundles are public client code and must never contain server credentials. Use public ingestion keys for analytics and keep server administration tokens in the API workspace.

All analytics keys in Expojet are scoped to the public environment:

# Standalone: .env.example / .env
# Monorepo: apps/mobile/.env.example / apps/mobile/.env

# PostHog
EXPO_PUBLIC_POSTHOG_KEY=phc_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
EXPO_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com

# Aptabase
EXPO_PUBLIC_APTABASE_KEY=A-EU-0000000000
EXPO_PUBLIC_APTABASE_HOST=https://api.aptabase.com

[!CAUTION] Keep POSTHOG_SECRET, POSTHOG_API_KEY, and APTABASE_SECRET out of the mobile directory, including names starting with EXPO_PUBLIC_. Doctor searches source and JSON files for known secret names. It cannot recognize renamed credentials or literal secret values.


Unified Typed Interface

Both PostHog and Aptabase adapters generate a standardized, typed client in src/analytics/index.ts:

import { useAnalytics, trackEvent, identifyUser, resetAnalytics } from "@/analytics";

// 1. Using the React hook inside components
export function CheckoutButton() {
  const { track } = useAnalytics();

  return (
    <Button
      title="Complete Purchase"
      onPress={() => {
        track("purchase_completed", { amount: 49.99, currency: "USD" });
      }}
    />
  );
}

// 2. Standalone tracking outside React render loops
trackEvent("app_launched", { platform: Platform.OS });

Provider Setup

The analytics provider is automatically configured in your application tree (src/analytics/provider.tsx).

Expo Router

Wrap your navigation hierarchy in app/_layout.tsx:

import { AnalyticsProvider } from "@/analytics";
import { Stack } from "expo-router";

export default function RootLayout() {
  return (
    <AnalyticsProvider>
      <Stack screenOptions={{ headerShown: false }} />
    </AnalyticsProvider>
  );
}

React Navigation

Wrap RootNavigator in src/App.tsx:

import { AnalyticsProvider } from "./analytics";
import { RootNavigator } from "./navigation/RootNavigator";

export function App() {
  return (
    <AnalyticsProvider>
      <RootNavigator />
    </AnalyticsProvider>
  );
}

Doctor Verification

Expojet's automated diagnostics verify that your analytics modules and configuration remain intact:

node packages/cli/dist/cli.js doctor

Checks executed:

  • Mobile analytics module: Validates presence of src/analytics/index.ts.
  • Mobile analytics provider: Validates presence of src/analytics/provider.tsx.
  • Mobile secret boundary: Flags known server variable names in mobile source and JSON files.

On this page