Skip to main content

πŸ“Š Analytics with PostHog

Shipnative integrates PostHog for powerful product analytics, session recording, and feature flagging. This guide will help you set up PostHog to understand user behavior and roll out features effectively.

PostHog Project Setup

1. Create Your PostHog Project

If you haven’t already, create a new project on the PostHog platform:
  1. Go to https://posthog.com/ and sign up.
  2. Create a new project.

2. Obtain API Key and Host

Once your project is ready, you’ll need your API key and host URL:
  1. In your PostHog project dashboard, navigate to Project Settings > API Keys.
  2. Copy your Project API Key.
  3. Note your PostHog Host (e.g., https://app.posthog.com or your self-hosted instance URL).

3. Configure Environment Variables

During the yarn setup process, you were prompted to enter these credentials. If you skipped this or need to update them, you can manually add them to your apps/app/.env file:
EXPO_PUBLIC_POSTHOG_API_KEY=your_posthog_api_key_here
EXPO_PUBLIC_POSTHOG_HOST=https://app.posthog.com # Or your self-hosted URL
Mock Mode: If these environment variables are not set, Shipnative will automatically use a mock PostHog service, allowing you to develop and test your analytics implementation without sending data to a live server.

Using PostHog in Your App

Shipnative provides a convenient useAnalytics hook and utility functions for interacting with PostHog. The useAnalytics hook (found in apps/app/app/hooks/useAnalytics.ts) simplifies common analytics tasks.
import { useAnalytics } from '@/hooks/useAnalytics'
import { useEffect } from 'react'

function MyComponent() {
  const { trackEvent, trackScreen, isFeatureEnabled } = useAnalytics()
  
  // Track screen view when component mounts
  useEffect(() => {
    trackScreen('MyScreen', { source: 'navigation' })
  }, [])
  
  // Track button click
  const handleClick = () => {
    trackEvent('button_clicked', {
      button_name: 'submit',
      screen: 'MyScreen',
    })
  }
  
  // Check if a feature flag is enabled
  const showNewFeature = isFeatureEnabled('new-feature')
  
  return (
    <View>
      <Button onPress={handleClick} title="Click Me" />
      {showNewFeature && <Text>New Feature Enabled!</Text>}
    </View>
  )
}

Using Utility Functions

For direct event tracking or identification outside of React components, you can use the utility functions:
import {
  trackEvent,
  trackScreen,
  identifyUser,
  clearUser,
  trackAuth,
  trackSubscription,
} from '@/utils/analytics'

// Track custom event
trackEvent('custom_event', { key: 'value' })

// Track screen
trackScreen('HomeScreen', { tab: 'feed' })

// Identify user
identifyUser('user-123', {
  email: 'user@example.com',
  name: 'John Doe',
})

// Clear user (on logout)
clearUser()

// Track authentication events
trackAuth.signupCompleted('user-123', { method: 'email' })
trackAuth.loginCompleted('user-123', { method: 'google' })
trackAuth.logout()

// Track subscription events
trackSubscription.started({ plan: 'pro_monthly' })
trackSubscription.completed({ plan: 'pro_monthly', price: 9.99 })

Direct PostHog Client Access

For advanced usage, you can directly access the PostHog client instance:
import { posthog } from '@/services/posthog'

posthog.track('event_name', { property: 'value' })
posthog.identify('user-id', { email: 'user@example.com' })
posthog.screen('ScreenName')
posthog.isFeatureEnabled('feature-flag')

Key PostHog Features

Event Tracking

Track user interactions and custom events within your app.
// Basic event
trackEvent('button_clicked')

// Event with properties
trackEvent('product_viewed', {
  product_id: '123',
  product_name: 'Widget',
  price: 29.99,
})
For more on event tracking, see the PostHog Event Tracking documentation.

Screen Tracking

Record which screens users visit and how they navigate.
// Basic screen
trackScreen('HomeScreen')

// Screen with properties
trackScreen('ProductScreen', {
  product_id: '123',
  source: 'search',
})

User Identification

Associate events with specific users to build user profiles.
// Identify user
identifyUser('user-123', {
  email: 'user@example.com',
  name: 'John Doe',
  plan: 'pro',
})

// Update user properties
import { posthog } from '../services/posthog'
posthog.setUserProperties({
  subscription_status: 'active',
  last_login: new Date().toISOString(),
})

// Clear user (on logout)
clearUser()
Learn more about identifying users in the PostHog Identify documentation.

Feature Flags

Control feature rollouts and A/B test new functionalities.
// Check if feature is enabled
if (isFeatureEnabled('new-feature')) {
  // Show new feature
}

// Get feature flag value (e.g., for A/B testing variants)
const variant = getFeatureFlag('test-variant') // Returns 'control' | 'variant-a' | 'variant-b'

// Listen for feature flag changes
posthog.onFeatureFlags?.((flags) => {
  console.log('Feature flags updated:', flags)
})
For detailed information on feature flags, visit the PostHog Feature Flags documentation.

Groups (B2B Analytics)

If your app serves businesses, you can track groups (e.g., companies, teams).
import { posthog } from '../services/posthog'
// Assign user to a group
posthog.group('company', 'company-123', {
  name: 'Acme Corp',
  plan: 'enterprise',
})
Explore group analytics in the PostHog Group Analytics documentation.

Testing Your Integration

Mock Mode Testing

  1. Ensure PostHog API keys are not set in your apps/app/.env file.
  2. Run your app (yarn ios or yarn android).
  3. Trigger some events or screen views.
  4. Check your console logs for messages like πŸ“Š [MockPostHog] Event: button_clicked, indicating that mock events are being captured.

Testing with Real Services

  1. Add your EXPO_PUBLIC_POSTHOG_API_KEY and EXPO_PUBLIC_POSTHOG_HOST to apps/app/.env.
  2. Run your app.
  3. Trigger events and screen views.
  4. Log in to your PostHog dashboard and navigate to:
    • Events: To see all captured events.
    • Persons: To see identified users and their properties.
    • Feature Flags: To test your feature flag configurations.

Best Practices

Event Naming

  • βœ… Good: button_clicked, product_viewed, checkout_completed (use snake_case)
  • ❌ Bad: ButtonClicked (use snake_case), click (too generic), user clicked the submit button on the checkout page (too verbose)

Event Properties

  • βœ… Good: Include relevant, flat properties.
    trackEvent('product_viewed', {
      product_id: '123',
      product_name: 'Widget',
      category: 'electronics',
      price: 29.99,
    })
    
  • ❌ Bad: Avoid deep nesting or redundant properties.
    trackEvent('product_viewed', {
      data: { /* nested object */ },  // Avoid deep nesting
      timestamp: Date.now(),  // PostHog adds this automatically
    })
    

Privacy

  • Don’t track PII (Personally Identifiable Information) without explicit user consent.
  • Anonymize sensitive data (e.g., credit card numbers, passwords).
  • Respect user preferences for opting out of tracking.
// Example: Anonymized payment event
trackEvent('payment_completed', {
  amount: 99.99,
  method: 'credit_card',
  // DO NOT include: card_number, cvv, etc.
})

// Example: Respecting user opt-out
import { posthog } from '../services/posthog'
if (userHasOptedOut) {
  posthog.optOut() // No events will be sent
}

Troubleshooting

Events not showing in PostHog

Problem: Events are tracked in your app but not appearing in the PostHog dashboard. Solution:
  • Double-check that your EXPO_PUBLIC_POSTHOG_API_KEY and EXPO_PUBLIC_POSTHOG_HOST are correct in apps/app/.env.
  • Verify your device has network connectivity.
  • Ensure your PostHog project is active and correctly configured.
  • Allow a few minutes for events to process and appear in the dashboard.
  • Check your browser’s developer console or device logs for any errors related to PostHog.

Mock mode in production

Problem: Your app is using mock PostHog services in production. Solution:
  • Verify that your apps/app/.env file exists and is correctly configured with EXPO_PUBLIC_POSTHOG_API_KEY and EXPO_PUBLIC_POSTHOG_HOST.
  • Ensure your environment variables are prefixed with EXPO_PUBLIC_.
  • Restart your Metro bundler with yarn app:start --clear to ensure environment variables are reloaded.
  • Review your build configuration to ensure .env variables are correctly bundled.