Analytics
Track workflow events, step timing, and user behavior with callback-based analytics.
Workflows fire optional callbacks at lifecycle moments — start, step transitions, skips, errors, completion. Wire them to any provider (Segment, PostHog, your own backend).
WorkflowAnalytics Interface
interface WorkflowAnalytics {
onWorkflowStart?(workflowId: string, context: WorkflowContext): void;
onWorkflowComplete?(workflowId: string, duration: number, data: any): void;
onWorkflowAbandon?(workflowId: string, currentStep: string, data: any): void;
onStepStart?(stepId: string, timestamp: number, context: WorkflowContext): void;
onStepComplete?(stepId: string, duration: number, data: any, context: WorkflowContext): void;
onStepSkip?(stepId: string, reason: string, context: WorkflowContext): void;
onError?(error: Error, context: WorkflowContext): void;
}| Parameter | Description |
|---|---|
workflowId | The id passed to flow.create. |
context | The WorkflowContext at event time (current step, data, visited steps). |
duration | Milliseconds: time spent on the step (onStepComplete) or since workflow start (onWorkflowComplete). |
data | The completed step's slice (onStepComplete) or the projected workflow data (onWorkflowComplete), both in the authored shape. |
reason | Why the step was skipped — currently always 'user_skip'. |
Configuration
Pass an analytics object to .configure():
const workflow = flow.create(rilay, 'onboarding', 'User Onboarding')
.step({ id: 'personal-info', title: 'Personal Info', formConfig: personalInfoForm })
.step({ id: 'preferences', title: 'Preferences', formConfig: preferencesForm, allowSkip: true })
.configure({
analytics: {
onWorkflowStart: (id, context) => {
track('workflow_started', { id, totalSteps: context.totalSteps });
},
onStepComplete: (stepId, duration, stepData) => {
track('step_complete', { stepId, duration, fields: Object.keys(stepData).length });
},
onStepSkip: (stepId, reason) => track('step_skipped', { stepId, reason }),
onWorkflowComplete: (id, duration, data) => {
track('workflow_complete', { id, seconds: Math.round(duration / 1000) });
},
onError: (error, context) => {
captureException(error, { workflowId: context.workflowId });
},
},
});Replace track / captureException with your provider's calls (analytics.track, posthog.capture, a fetch to your backend, …).
Event Timing
| Event | When it fires |
|---|---|
onWorkflowStart | Once, when the workflow reaches its first interactive step (after any async persistence load settles). |
onStepStart | Each time the active step changes, including the first step. |
onStepComplete | On forward navigation away from a step. Not fired on backward navigation, for skipped steps, or for the last step — completion is carried by onWorkflowComplete. |
onStepSkip | When skipStep() is called. |
onWorkflowComplete | After the last step is submitted. data is the same projection onComplete receives — only answered visible steps, no placeholders. |
onWorkflowAbandon | On unmount, if the workflow started but never completed. |
onError | On any workflow error path (below). |
Error paths
Every workflow error routes through onError — and the global monitor, when initialized: step-transition failures, onAfterValidation throws, submission throws, and persistence save/load/remove failures (as WorkflowPersistenceError). A validation error that blocks Next is not an error path and fires no onError.
Global Monitoring
If a global monitor is initialized (initializeMonitoring from @rilaykit/core), all analytics events are also forwarded to it as workflow_navigation events carrying WorkflowPerformanceMetrics:
interface WorkflowPerformanceMetrics {
timestamp: number;
duration: number;
workflowId: string;
stepCount: number;
currentStepIndex: number;
navigationDuration: number;
persistenceDuration?: number;
conditionEvaluationDuration: number;
}Step skips, slow navigation (> 1 s), and slow condition evaluation (> 100 ms) are flagged medium priority; errors go through monitor.trackError.
Monitor integration is opt-in. Without a global monitor, analytics callbacks work standalone with no overhead.
useWorkflowAnalytics Hook
WorkflowProvider drives all of the above through the internal useWorkflowAnalytics hook — start times, step durations, and event dispatch are automatic; you never call it yourself. It returns manual escape hatches (trackStepSkip, trackError, trackNavigation, trackConditionEvaluation) used by the navigation and submission hooks.