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.
| Event | Properties | When it is sent |
|---|---|---|
onboarding_completed | notifications: 'enabled' | 'skipped' | The last onboarding step saves the answers. |
sign_up_completed | method: 'email', needs_email_confirmation: boolean | Supabase accepts the sign-up request. |
sign_in_completed | method: 'email' | 'apple' | 'google' | Sign-in succeeds. |
signed_out | None | The sign-out hook completes. |
account_deleted | None | The delete-account function succeeds. |
chat_message_sent | model: string, has_image: boolean, new_thread: boolean | The chat SDK calls onFinish for a pending message. |
image_generated | model: string, style: string, aspect_ratio: string | Image generation succeeds. |
image_generation_blocked | reason: 'pro_required' | Image generation returns pro_required. |
scan_completed | None | A scan succeeds. |
paywall_shown | placement: 'onboarding_end' | 'quota_exceeded' | A paywall placement is requested. |
pro_status_changed | is_pro: boolean | The 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:
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'.