Splitting
Splits let you filter a funnel by one (or more) conditions, so you can see conversion rates for specific cohorts instead of the blended whole-funnel average.
For conceptual background, start with Concepts → Splitting & Extensions. This page is the complete reference for every split condition.
How splits are evaluated
When you add a split, it becomes a node on the canvas between two stages. Everything
downstream of the split is filtered. Internally, FunnelFizz translates the split condition
into a SQL WHERE clause against the trackingEvents or profileSubscriptions tables.
Conditions chain with AND. If you nest splits, each additional split adds another AND clause — a visitor must match every split in the chain to appear in the deepest branch.
The eight split conditions
Traffic source
Internal name: traffic_source
Filters on: trackingEvents.referrer (ILIKE pattern match)
Use case: "show me only traffic that came from reddit.com."
Configuration:
- Domain — a substring of the referrer URL. E.g.,
reddit.commatcheshttps://www.reddit.com/r/saas/...andhttps://old.reddit.com/....
Example value: twitter.com, news.ycombinator.com, producthunt.com.
UTM parameter
Internal name: utm
Filters on: trackingEvents.utm (JSONB field)
Use case: "show me only traffic from my summer campaign."
Configuration:
- UTM key — pick one of:
utm_source,utm_medium,utm_campaign,utm_content,utm_term. - Value — exact string match.
Example value: utm_campaign = summer-launch, utm_source = newsletter.
Channel
Internal name: channel
Filters on: trackingEvents.channel (server-side categorization)
Use case: "show me only organic search traffic."
Categories:
direct— no referrer, no UTM.organic_search— Google, Bing, DuckDuckGo, Brave, etc.social— X, LinkedIn, Reddit, TikTok, Instagram, YouTube, Facebook.paid—utm_mediumincpc/paid/ads, or recognized ad networks.email—utm_medium = emailor email-client referrers.ai_llm— chatgpt.com, claude.ai, perplexity.ai, gemini.google.com, copilot.microsoft.com.referral— everything else with a referrer.
Configuration:
- Channel — pick one from the list above.
Device type
Internal name: device_type
Filters on: trackingEvents.deviceType (UA-parsed)
Use case: "is my mobile flow converting at a different rate than desktop?"
Options: desktop, mobile, tablet.
Country
Internal name: geography
Filters on: trackingEvents.country (ISO 3166-1 alpha-2, from MaxMind GeoIP)
Use case: "only show me US + Canadian traffic."
Configuration:
- Country — pick from a 249-country dropdown.
Note: we don't currently support multi-country splits as a single branch. For "US + CA," use two splits stacked together, or filter in the dashboard exports.
Custom event
Internal name: custom_event
Filters on: trackingEvents.props->>'goal' (JSONB extract)
Use case: "only show me people who clicked the pricing CTA."
Configuration:
- Event name — pick from the list of events your tracking script has observed.
How to define events: fire them from your site with
funnelfizz('event', 'pricing_clicked') or mark elements with data-fn-goal="pricing_clicked".
Once you've fired an event, it appears in the dropdown here.
A/B variant
Internal name: ab
Filters on: trackingEvents.abVariant
Use case: "compare conversion rates of my hero copy variants."
How it works: when a visitor lands on your site with ?ab=<variant> in the URL, the
tracking script stores that variant in a cookie and tags every subsequent event with it.
Configuration:
- Variant name — the string you used in the URL. E.g.,
?ab=frog→ filter valuefrog.
Example: you send two newsletter campaigns, one with ?ab=frog and one with ?ab=elk.
Split on ab = frog and ab = elk side by side to see which variant drove more conversions.
Stripe product
Internal name: stripe_product
Filters on: ProfileSubscription tables (not tracking events)
Use case: "show me only people on the Pro tier."
Configuration:
- Product — pick from the list of Stripe products in your connected account.
Limitation: this split only applies at the TRIAL and CUSTOMER stages. Since it's a subscription-level filter, it doesn't make sense at AWARENESS or CONSIDERATION (there's no Stripe data yet).
Nesting splits
You can split a split. "US visitors from YouTube on mobile" is a three-level chain:
- Country = US → US branch.
- On the US branch, Traffic source = youtube.com → US + YouTube branch.
- On that branch, Device = mobile → US + YouTube + mobile branch.
The canvas auto-lays-out nested branches so they don't overlap. There's no hard depth limit but in practice three levels is about as much as remains readable.
Compound conditions (within a single split)
Each split node has exactly one condition. If you want "A AND B," that's two splits stacked. If you want "A OR B," that's two sibling splits (you'll see both branches on the canvas).
There is no "NOT" condition today. If you want "everyone except US traffic," split by country and look at the other unfiltered stages — or use the opposite country list.
Split metrics
Each split branch shows:
- Profiles in the branch at each downstream stage.
- Conversion rates between stages, computed only for matching profiles.
- Channel, device, country breakdown of the branch's traffic.
- Top referrers and Top UTMs within the branch.
All metrics re-compute when you change the time window.
Adding a split in the UI
- On the canvas, click the + button between any two stages (or between split and stage).
- Pick a condition from the modal.
- Fill in the value.
- (Optional) Name the branch — shows up as a label on the canvas.
- Color the branch — each split auto-picks a tint to stay visually distinct.
- Save.
The branch fills in within a second or two.
Removing a split
Right-click the split node → Delete. Everything downstream of it collapses. The main funnel stays intact. Historical data isn't deleted — if you re-add the same split later, the numbers re-appear.
Behind the scenes
The SplitModal in src/components/canvas/SplitModal.tsx lists every condition. The SQL
translation happens in src/lib/metrics/split-filter.ts. If you're curious how a condition
ends up as a WHERE clause, that's where to look.
Patterns we see a lot
- Source-of-truth split. Main funnel + one split per top traffic source. Answers "where does my best traffic come from?"
- UTM campaign split. Each campaign gets its own branch. Answers "which campaign actually converted, not just got clicks?"
- A/B variant split. Paired branches comparing two hero copy variants. Combined with percentage-split automations for email A/B testing.
- Country + device cross. Two-level nest. Answers "is my US mobile experience broken?"
Next: Extensions → — the upsell tracking pipeline.