Skip to content
Goodspeed
← Blog
Technicalby Goodspeed Team

Setting Up PostHog Analytics in Mobile Apps

A practical guide to PostHog analytics in React Native, covering event taxonomy, custom properties, dashboards, and privacy-friendly tracking.

Why PostHog for Mobile

Most analytics tools are built for web. PostHog started there too, but it's become one of the best options for mobile apps. Here's why:

  • Self-hostable or cloud: Choose your data residency
  • Event-based: Track anything, not just page views
  • Feature flags: Roll out features to a percentage of users
  • Session replay: Watch how users navigate your app (web only for now, mobile coming)
  • Generous free tier: 1 million events per month

We track analytics in every app we build. PostHog is our default choice. Here's exactly how to set it up in React Native.


Installation

npx expo install posthog-react-native

Initialization (The Right Way)

Empty API key crash

Creating a PostHog instance with an empty string will crash your app. Always guard against missing environment variables, especially in development.

The most common mistake with PostHog in React Native is initializing with an empty API key. If the EXPO_PUBLIC_POSTHOG_API_KEY environment variable is not set (common in development), creating a PostHog instance with an empty string will crash your app.

// lib/posthog.ts
import PostHog from 'posthog-react-native';

const apiKey = process.env.EXPO_PUBLIC_POSTHOG_API_KEY;
const host = process.env.EXPO_PUBLIC_POSTHOG_HOST || 'https://us.i.posthog.com';

// Only create the client if we have a valid API key
export const posthog = apiKey
  ? new PostHog(apiKey, { host })
  : null;

Guard every analytics call:

export function capture(event: string, properties?: Record<string, unknown>) {
  if (!posthog) return;
  posthog.capture(event, properties);
}

export function identify(userId: string, properties?: Record<string, unknown>) {
  if (!posthog) return;
  posthog.identify(userId, properties);
}

export function reset() {
  if (!posthog) return;
  posthog.reset();
}

This pattern ensures your app works in any environment, with or without analytics configured.


Designing Your Event Taxonomy

Before you start tracking, plan your events. A messy event taxonomy is worse than no analytics at all. You'll spend more time cleaning data than learning from it.

The Event Naming Convention

Use object_action format

Stick to snake_case with object_action naming: thread_viewed, purchase_completed, paywall_shown. Avoid camelCase, vague names like click_button_3, or free-text names that break queries.

Use object_action format with snake_case:

  • app_opened
  • sign_up_completed
  • thread_viewed
  • thread_bookmarked
  • paywall_shown
  • purchase_completed
  • settings_theme_changed

Not:

  • AppOpened (inconsistent with PostHog conventions)
  • click_button_3 (meaningless without context)
  • user did a thing (not queryable)

Essential Events for Any Mobile App

Every app should track these at minimum:

// App lifecycle
capture('app_opened');
capture('app_backgrounded');

// Authentication
capture('sign_up_started', { method: 'email' });
capture('sign_up_completed', { method: 'email' });
capture('sign_in_completed', { method: 'google' });
capture('sign_out');

// Core feature usage
capture('feature_used', { feature: 'thread_analysis', screen: 'dashboard' });

// Monetization
capture('paywall_shown', { trigger: 'feature_gate', feature: 'ai_analysis' });
capture('purchase_started', { product: 'premium_monthly' });
capture('purchase_completed', { product: 'premium_monthly', price: 4.99 });

// Errors
capture('error_occurred', { type: 'api_error', endpoint: '/threads', status: 500 });

Custom Properties

Attach context to every event. Properties answer "what happened" and "where":

capture('thread_viewed', {
  thread_id: thread.id,
  subreddit: thread.subreddit,
  score: thread.score,
  source: 'trending_feed', // How did they get here?
});

Property design principles

Good properties identify the object (ID, name, category), describe the context (screen, source, trigger), and include relevant metrics (score, count, duration). Avoid PII without consent, high-cardinality free text, and redundant user IDs that PostHog already handles.


User Identification

Link anonymous events to authenticated users:

// After sign-in or sign-up
identify(user.id, {
  email: user.email,
  plan: 'free', // or 'premium'
  signed_up_at: user.created_at,
  platform: Platform.OS,
});

Call identify once after authentication. PostHog will associate all previous anonymous events with this user (if you're using the same device).

When the user signs out:

reset(); // Clears the identified user, creates a new anonymous ID

Building a useAnalytics Hook

Wrap PostHog in a custom hook that makes tracking easy across your app:

// hooks/useAnalytics.ts
import { useCallback } from 'react';
import { capture, identify, reset } from '../lib/posthog';

export function useAnalytics() {
  const trackEvent = useCallback((event: string, properties?: Record<string, unknown>) => {
    capture(event, {
      ...properties,
      timestamp: new Date().toISOString(),
    });
  }, []);

  const trackScreen = useCallback((screenName: string) => {
    capture('screen_viewed', { screen: screenName });
  }, []);

  const identifyUser = useCallback((userId: string, traits?: Record<string, unknown>) => {
    identify(userId, traits);
  }, []);

  const resetUser = useCallback(() => {
    reset();
  }, []);

  return { trackEvent, trackScreen, identifyUser, resetUser };
}

Setting Up Dashboards

In PostHog, create a dashboard for your app with these panels:

Daily Active Users

  • Event: app_opened
  • Aggregation: Unique users per day
  • This is your north star metric

Signup Funnel

  1. sign_up_started
  2. sign_up_completed
  3. onboarding_completed
  4. first_feature_used

Seeing where users drop off in this funnel tells you exactly where to focus improvement.

Feature Adoption

  • Event: feature_used
  • Breakdown by: feature property
  • Shows which features get used and which are ignored

Revenue Funnel

  1. paywall_shown
  2. purchase_started
  3. purchase_completed

The conversion rate from paywall shown to purchase completed tells you if your pricing and positioning work.

Error Tracking

  • Event: error_occurred
  • Breakdown by: type and endpoint
  • Alert if the count exceeds a threshold

Feature Flags

PostHog feature flags let you roll out features gradually:

import { useFeatureFlag } from 'posthog-react-native';

function MyComponent() {
  const showNewDashboard = useFeatureFlag('new-dashboard-v2');

  if (showNewDashboard) {
    return <NewDashboard />;
  }
  return <OldDashboard />;
}

Use feature flags for:

  • Gradual rollouts (10% of users, then 50%, then 100%)
  • A/B testing different UI variants
  • Kill switches for broken features
  • Beta access for specific user segments

Privacy Considerations

GDPR Compliance

Don't track until the user consents. PostHog's optOut flag makes this straightforward:

// Before consent
posthog?.optOut();

// After user grants consent
posthog?.optIn();

Data Minimization

Only track what you need. Don't capture personal information in event properties unless it's necessary for analysis. PostHog lets you configure which properties to redact on the server side.

Retention Policies

Set data retention in PostHog's project settings. You probably don't need event data from 3 years ago. 12 months is a reasonable default.


Common Mistakes

Start small, add later

Begin with 10-15 core events. You can always add events later; removing noise from existing data is harder. An event without properties is nearly useless.

  1. Tracking too many events. Start with 10-15 core events. Add more as questions arise. You can always add events later; removing noise from existing data is harder.

  2. Not tracking enough context. An event without properties is nearly useless. "button_clicked" tells you nothing. "purchase_started with product=premium_yearly, trigger=upgrade_banner, screen=settings" tells you everything.

  3. Forgetting to test in production. Verify events are flowing in the PostHog dashboard after your production deploy. Development and production often have different API keys.

  4. Ignoring the data. The best analytics setup is worthless if nobody looks at the dashboards. Set a weekly reminder to review your core metrics.

Key takeaway

Good analytics starts with a clean event taxonomy (object_action format, meaningful properties) and a null-safe initialization pattern. Set up four dashboards (DAU, signup funnel, feature adoption, revenue funnel), start with 10-15 events, and review weekly.

For more on building production apps with proper analytics, check out our tech stack and the complete build pipeline.

Subscribe to The Signal

The top 5 scored app ideas, delivered fresh.

Ready to build?

Score your first idea free. See the pipeline in action.