Analytics integration
PostHog and Wire AI on React Native
Wire AI emits a typed lifecycle event on every turn of the flow, and toAnalyticsEvent maps each one to a canonical wire_onboarding_* name so the same funnel reads identically in every app you ship. Piping that into PostHog is one prop. The part that catches people is the last step: there is no completed event on onEvent, because the kit signals completion through onComplete instead. Wire only onEvent and PostHog shows a funnel that starts and never finishes, which reads as a total drop on every cohort.
Install
$npxexpo install posthog-react-native expo-file-system expo-application expo-device expo-localization$npminstall @wireai/activationAPI surface on this page read against @wireai/activation 0.13.5 (npm latest, read 2026-08-04) and posthog-react-native 4.61.2 (npm latest, read 2026-08-02). These are the versions the calls below were checked against, not a claim that this exact file is running in production somewhere.
Map the kit events to canonical names
toAnalyticsEvent takes the kit's OnboardingEvent union and returns a name plus params. The switch is exhaustive over that union on purpose, so a new event type becomes a compile error in the kit rather than a silent gap in your dashboard.
WIRE_ONBOARDING_EVENTS.completed is a constant with no matching kit event. Capture it from onComplete, using the same constant, and the terminal step keeps the canonical name instead of whatever you would have typed by hand at 1am.
1import { usePostHog } from "posthog-react-native";
2import {
3 WireOnboarding,
4 toAnalyticsEvent,
5 WIRE_ONBOARDING_EVENTS,
6} from "@wireai/activation";
7
8import { wireConfig } from "@/lib/wire";
9import { StaticOnboarding } from "@/components/StaticOnboarding";
10
11export function OnboardingScreen({ onDone }: { onDone: () => void }) {
12 const posthog = usePostHog();
13
14 return (
15 <WireOnboarding
16 config={wireConfig}
17 onEvent={(event) => {
18 const mapped = toAnalyticsEvent(event);
19 posthog.capture(mapped.name, mapped.params);
20 }}
21 onComplete={(result) => {
22 // No kit event for this one. Capture it or the funnel has no end.
23 posthog.capture(WIRE_ONBOARDING_EVENTS.completed, {
24 answers: Object.keys(result.answers).length,
25 });
26 onDone();
27 }}
28 fallbackFlow={<StaticOnboarding onDone={onDone} />}
29 />
30 );
31}Build the funnel on started OR resumed
Pass the storage prop and the kit persists its session seed, so an app kill mid-onboarding resumes the same backend session rather than minting a new one. When that happens the kit emits resumed instead of started, deliberately, so a host funnel does not double-count one session as two starts.
The consequence for PostHog is that a funnel whose first step is only wire_onboarding_started undercounts. Use both names for the entry step and the completion rate stops drifting on the cohort of people whose phone rang mid-flow.
1import AsyncStorage from "@react-native-async-storage/async-storage";
2import { WireOnboarding } from "@wireai/activation";
3
4import { wireConfig } from "@/lib/wire";
5
6export function OnboardingScreen({ onDone }: { onDone: () => void }) {
7 return (
8 <WireOnboarding
9 config={wireConfig}
10 // storage turns an app kill into "resumed",
11 // not a phantom second "started"
12 storage={AsyncStorage}
13 onComplete={onDone}
14 />
15 );
16}What breaks
The failure modes when PostHog and Wire AI share an app. None of them throws, which is why they survive code review.
onEvent has no completed case
The kit signals completion through onComplete, not onEvent, so toAnalyticsEvent will never hand you wire_onboarding_completed. Capture it explicitly from onComplete with the exported constant. Every funnel that looks like a 100% drop-off is this bug.
resumed fires instead of started
With storage set, a restored session emits resumed and skips started on purpose. Count both as the funnel entry or your completion rate is quietly lower than reality.
userId is capped at 128 characters and must be pseudonymous
The kit trims and truncates at 128 rather than rejecting, so an over-long id gets silently cut and stops matching your user table. It is an opaque id you own, never an email, a name, or a phone number.
The AI can degrade, and degrading is an event
retry and fallback both come through onEvent with a reason of backend or timeout. Those are the two lines that tell you whether a bad activation week was the flow or the model. Chart them next to the funnel, not in a separate dashboard nobody opens.
Questions people actually ask
Do I still need Wire AI analytics if PostHog is already in the app?
The kit reports its own funnel to the Wire server because the experiment engine needs those events to assign and score arms. Sending the same moments to PostHog costs one prop and keeps your existing dashboards whole.
Can I use my PostHog distinct id as the Wire user id?
Yes, as long as it is pseudonymous and inside 128 characters. The userId prop also binds late: change it mid-session after the user registers and the kit emits an identify event against the live session.
Does this add a second analytics SDK to my bundle?
No. toAnalyticsEvent and the event constants are plain functions and strings. Your transport stays PostHog.
Sources
- PostHog: React Native library docs (read 2026-07-23)
Wire it into your app
Analytics is free to 299k events a month, forever, on any app. 15 founder seats get the whole engine free, 4 filled.