Funnel Analysis
Track and analyze multi-step user journeys to identify conversion opportunities and drop-off points.
Overview
Funnel analysis helps you understand:
- How users progress through key flows
- Where users drop off
- Conversion rates at each step
- Optimization opportunities
Creating Funnels
Define a Funnel
import { createFunnel } from '@stacksjs/ts-analytics'
const checkoutFunnel = createFunnel({
id: 'checkout',
name: 'Checkout Flow',
steps: [
{
name: 'View Product',
type: 'pageview',
pattern: '/product/*',
matchType: 'startsWith',
},
{
name: 'Add to Cart',
type: 'event',
pattern: 'add_to_cart',
matchType: 'exact',
},
{
name: 'View Cart',
type: 'pageview',
pattern: '/cart',
matchType: 'exact',
},
{
name: 'Checkout',
type: 'pageview',
pattern: '/checkout',
matchType: 'startsWith',
},
{
name: 'Purchase',
type: 'pageview',
pattern: '/order/confirmation',
matchType: 'startsWith',
},
],
})
Built-in Funnels
ts-analytics includes pre-built funnels for common use cases:
E-commerce Checkout
import { ecommerceCheckoutFunnel } from '@stacksjs/ts-analytics'
// Steps: Product View → Add to Cart → Cart → Checkout → Purchase
SaaS Signup
import { saasSignupFunnel } from '@stacksjs/ts-analytics'
// Steps: Landing → Signup → Email Verify → Onboarding → Activated
Content Engagement
import { contentEngagementFunnel } from '@stacksjs/ts-analytics'
// Steps: Article View → Scroll 50% → Scroll 100% → Share/Subscribe
Analyzing Funnels
FunnelAnalyzer
import { FunnelAnalyzer } from '@stacksjs/ts-analytics'
const analyzer = new FunnelAnalyzer()
// Analyze user journeys against a funnel
const analysis = analyzer.analyze(checkoutFunnel, userJourneys)
console.log(analysis)
// {
// funnel: { id: 'checkout', name: 'Checkout Flow', ... },
// steps: [
// { name: 'View Product', entered: 10000, completed: 10000, dropOff: 0, rate: 1.0 },
// { name: 'Add to Cart', entered: 10000, completed: 3500, dropOff: 6500, rate: 0.35 },
// { name: 'View Cart', entered: 3500, completed: 2800, dropOff: 700, rate: 0.80 },
// { name: 'Checkout', entered: 2800, completed: 2100, dropOff: 700, rate: 0.75 },
// { name: 'Purchase', entered: 2100, completed: 1500, dropOff: 600, rate: 0.71 },
// ],
// totalConversionRate: 0.15,
// totalEntries: 10000,
// totalConversions: 1500,
// }
Calculate Drop-Off Rate
import { calculateDropOffRate } from '@stacksjs/ts-analytics'
const dropOff = calculateDropOffRate(3500, 2800)
// 0.20 (20% drop-off)
Identify Drop-Off Points
import { identifyDropOffPoints } from '@stacksjs/ts-analytics'
const problems = identifyDropOffPoints(analysis, {
threshold: 0.30, // Flag steps with >30% drop-off
})
// Returns steps where drop-off exceeds threshold
User Journeys
Collecting Journey Data
// Track user journey through the session
const journey = {
visitorId: 'visitor-hash',
sessionId: 'session-123',
steps: [
{ type: 'pageview', path: '/product/shoes', timestamp: '...' },
{ type: 'event', name: 'add_to_cart', timestamp: '...' },
{ type: 'pageview', path: '/cart', timestamp: '...' },
{ type: 'pageview', path: '/checkout', timestamp: '...' },
// User abandoned here
],
}
Query Journeys from Store
// Get sessions with their events for funnel analysis
const sessionsCommand = store.querySessionsCommand('my-site', {
start: new Date('2024-01-01'),
end: new Date('2024-01-31'),
})
const sessions = await executeCommand(sessionsCommand)
// For each session, get the events
const journeys = await Promise.all(
sessions.Items.map(async (session) => {
const eventsCommand = store.querySessionEventsCommand(session.id)
const events = await executeCommand(eventsCommand)
return {
sessionId: session.id,
steps: events.Items,
}
})
)
Dashboard Component
FunnelChart
<template>
<FunnelChart
:steps="[
{ name: 'View Product', count: 10000 },
{ name: 'Add to Cart', count: 3500 },
{ name: 'View Cart', count: 2800 },
{ name: 'Checkout', count: 2100 },
{ name: 'Purchase', count: 1500 },
]"
:show-percentages="true"
:show-drop-off="true"
/>
</template>
Funnel Report
<template>
<div class="funnel-report">
<h2>{{ funnel.name }}</h2>
<div class="overall-stats">
<StatCard
title="Conversion Rate"
:value="analysis.totalConversionRate"
format="percentage"
/>
<StatCard
title="Total Conversions"
:value="analysis.totalConversions"
/>
</div>
<FunnelChart :steps="analysis.steps" />
<DataTable
:columns="[
{ key: 'name', label: 'Step' },
{ key: 'entered', label: 'Entered' },
{ key: 'completed', label: 'Completed' },
{ key: 'rate', label: 'Rate', format: 'percentage' },
{ key: 'dropOff', label: 'Drop-off' },
]"
:data="analysis.steps"
/>
</div>
</template>
Funnel Report Generation
Text Report
import { formatFunnelReport } from '@stacksjs/ts-analytics'
const report = formatFunnelReport(analysis)
console.log(report)
// Output:
// Checkout Flow Funnel Analysis
// =============================
//
// Overall Conversion: 15.0% (1,500 / 10,000)
//
// Step Breakdown:
// 1. View Product: 10,000 → 10,000 (100.0%)
// 2. Add to Cart: 10,000 → 3,500 (35.0%) ⚠️ High drop-off
// 3. View Cart: 3,500 → 2,800 (80.0%)
// 4. Checkout: 2,800 → 2,100 (75.0%)
// 5. Purchase: 2,100 → 1,500 (71.4%)
//
// Recommendations:
// - Add to Cart step has 65% drop-off - consider improving product pages
Best Practices
1. Define Clear Steps
Each step should represent a distinct user action:
// Good: Clear, measurable steps
const goodFunnel = createFunnel({
steps: [
{ name: 'Landing Page', type: 'pageview', pattern: '/' },
{ name: 'Pricing Page', type: 'pageview', pattern: '/pricing' },
{ name: 'Signup Click', type: 'event', pattern: 'signup_click' },
{ name: 'Signup Complete', type: 'pageview', pattern: '/welcome' },
],
})
// Avoid: Ambiguous or overlapping steps
2. Order Steps Sequentially
Funnel steps should follow the natural user flow:
// ✅ Correct order
const funnel = createFunnel({
steps: [
{ name: 'Browse', ... },
{ name: 'Add to Cart', ... },
{ name: 'Checkout', ... },
{ name: 'Purchase', ... },
],
})
3. Set Appropriate Thresholds
Define what constitutes a problematic drop-off:
// Different thresholds for different funnel types
const thresholds = {
checkout: 0.25, // Expect ≤25% drop-off per step
signup: 0.35, // More drop-off acceptable
onboarding: 0.20, // Less drop-off expected
}
4. Segment Your Analysis
Analyze funnels by user segments:
// Analyze by device type
const mobileJourneys = journeys.filter(j => j.device === 'mobile')
const desktopJourneys = journeys.filter(j => j.device === 'desktop')
const mobileAnalysis = analyzer.analyze(funnel, mobileJourneys)
const desktopAnalysis = analyzer.analyze(funnel, desktopJourneys)
// Compare conversion rates
console.log('Mobile:', mobileAnalysis.totalConversionRate)
console.log('Desktop:', desktopAnalysis.totalConversionRate)
Next Steps
- Goal Tracking - Track individual conversions
- Dashboard Components - Display funnel charts
- DynamoDB Design - Data storage patterns