▲ Cairn

Events & Analytics

Every transition is a typed event. Pipe the stream to PostHog, Segment, or anything.

Events & Analytics

Analytics isn't bolted on in Cairn — it's the same event stream the engine uses internally. Every meaningful transition emits a typed event, and you subscribe to one, some, or all of them.

The event stream

EventFired when
flow:startA flow begins fresh
flow:resumeA persisted flow resumes mid-progress
step:enterA step becomes active
step:exitA step is left
flow:completeThe flow reaches its end
flow:skipThe user skips the flow
flow:dismissThe flow is dismissed
context:updateContext is patched via setContext

Every payload includes the flowId and the current state snapshot; step events also include the step.

Subscribe to everything (onAny)

This is how analytics adapters capture the whole funnel in one place:

engine.onAny((event) => {
  analytics.track(`cairn.${event.type}`, {
    flowId: event.flowId,
    step: event.state.currentStepId,
    stepIndex: event.state.stepIndex,
  });
});

event.type is a discriminated union, so TypeScript narrows the payload:

engine.onAny((event) => {
  if (event.type === "step:enter") {
    // event.step is typed here
    console.log("entered", event.step.id);
  }
});

Subscribe to one event (on)

For targeted side effects:

const off = engine.on("flow:complete", ({ state }) => {
  confetti();
  markOnboardingDone(state.flowId);
});

// later
off(); // unsubscribe

Measuring funnels

Because step:enter fires for every step the user actually reaches (skipped guarded steps don't fire), the event stream is your onboarding funnel:

  • Drop-off — steps with many step:enter but few advancing to the next
  • Completion rateflow:complete vs flow:start + flow:resume
  • Skip rateflow:skip / flow:dismiss per flow

No extra instrumentation: wire onAny to your analytics provider once and every flow is measured.

On this page