Skip to Content

Analytics usage

Use trackEvent to send typed product events from your screens and hooks.

Complete Analytics setup first. App code imports from @/services/analytics; the PostHog SDK is confined to the analytics service and AnalyticsProvider.

What’s already tracked

The event names and properties are defined in src/services/analytics/events.ts.

EventPropertiesWhen it is sent
onboarding_completednotifications: 'enabled' | 'skipped'The last onboarding step saves the answers.
sign_up_completedmethod: 'email', needs_email_confirmation: booleanSupabase accepts the sign-up request.
sign_in_completedmethod: 'email' | 'apple' | 'google'Sign-in succeeds.
signed_outNoneThe sign-out hook completes.
account_deletedNoneThe delete-account function succeeds.
chat_message_sentmodel: string, has_image: boolean, new_thread: booleanThe chat SDK calls onFinish for a pending message.
image_generatedmodel: string, style: string, aspect_ratio: stringImage generation succeeds.
image_generation_blockedreason: 'pro_required'Image generation returns pro_required.
scan_completedNoneA scan succeeds.
paywall_shownplacement: 'onboarding_end' | 'quota_exceeded'A paywall placement is requested.
pro_status_changedis_pro: booleanThe RevenueCat provider observes a change in Pro status after its initial value.

paywall_shown records a request to show a paywall, so it can appear even when payments are unconfigured or a Superwall campaign presents nothing. Use the paywall vendor’s reporting to measure displayed paywalls.

notifications: 'enabled' records the onboarding button the user chose. It does not confirm that the operating system granted permission.

After sign-in, SessionProvider identifies the user with their Supabase ID and email. Product events omit emails, prompts, message text, images and file paths.

Adding events

Define the event

Add a property to the existing AnalyticsEvents type:

src/services/analytics/events.ts
export type AnalyticsEvents = { // Keep the existing events. report_exported: { format: 'pdf' | 'csv' }; };

Use snake_case for event and property names. Keep user-entered content out of the properties.

Track the completed action

Call the helper after the export succeeds:

import { trackEvent } from '@/services/analytics'; trackEvent('report_exported', { format: 'pdf' });

TypeScript checks the event name and its property shape. An event with no properties uses undefined in the type and takes no second argument.

Verify

Run yarn typecheck, perform the action, then find report_exported in PostHog and check its format property.

If no event arrives, confirm that both EXPO_PUBLIC_POSTHOG_API_KEY and EXPO_PUBLIC_POSTHOG_HOST are set. A missing value disables analytics without an error.

Screen views

The app does not call analytics.screen() explicitly. To track route changes, follow Expo Router’s screen-tracking pattern  in a component mounted under the analytics provider:

import { useEffect } from 'react'; import { usePathname } from 'expo-router'; import { analytics } from '@/services/analytics'; export function ScreenTracking() { const pathname = usePathname(); useEffect(() => { analytics.screen(pathname); }, [pathname]); return null; }

Check that a route change produces one screen event. Avoid adding another tracker if you have already configured screen tracking elsewhere.

Swapping the provider

Write an adapter

Create src/services/analytics/<vendor>.adapter.ts implementing AnalyticsPort. Use posthog.adapter.ts as the example.

Select the adapter

Update src/services/analytics/analytics.ts to create your adapter when the vendor is configured and use noopAnalytics otherwise. Keep the exports in index.ts so existing imports continue to work.

Update the provider and lint rules

Replace or remove the PostHog-specific wrapper in src/provider/AnalyticsProvider.tsx. If the new vendor needs a provider, mount it there.

In eslint.config.js, update vendorOwners and the restricted SDK imports so the new SDK is confined to its integration files.

Verify

Run yarn typecheck and yarn lint. Sign in and confirm that sign_in_completed arrives in the new service with method: 'email'.

Last updated on