Targeting flows
Country, device, UTM, cookie, time and visitor conditions as a node graph, in the dashboard and in the config.
Who enters an experiment, and with which split, can depend on country, device, browser, UTM parameters, cookies, time, or whether the visitor is new or returning. In SplitHub this is a small node graph, the flow, on the experiment.
In the dashboard
Open an experiment → Targeting. The editor has three node types:
- Condition: one or more conditions, combined with
match: 'all'or'any'. Fields includecountry,region,city,device,browser,os,language,referrer,url,path,utm_source,utm_medium,utm_campaign,utm_term,utm_content,query,cookie,visitor,hour,weekday. Forqueryandcookie, setkeyto the parameter or cookie name. Operators:is,is_not,in,not_in,contains,not_contains,starts_with,matches,exists,not_exists. Values are strings;inuses a comma-separated list. - Split: weighted outputs; each output goes to a variant or to another node.
- Variant / Skip: the leaf.
Skipmeans "not in the experiment": the visitor sees the control and no exposure is recorded.
A toggle switches to a simple list view (sections top to bottom, drag to reorder) for flows that are just "if country in EU → split A, else → split B". While the flow editor is open, variant editing is locked so the two cannot drift; on save the editor collapses to a one-line summary.
In the config
The same graph serializes as flow on the experiment and comes into splithub.config.ts with npx splithub pull:
'hero-title': {
kind: 'value',
status: 'running',
variants: { control: { weight: 1, value: 'A' }, bold: { weight: 1, value: 'B' } },
flow: {
start: 'geo',
nodes: {
geo: { type: 'condition', conditions: [{ field: 'country', op: 'in', value: 'DE,AT,CH' }], then: 'eu', else: 'rest' },
eu: { type: 'split', outputs: [{ weight: 50, to: 'control' }, { weight: 50, to: 'bold' }] },
rest: { type: 'split', outputs: [{ weight: 90, to: 'control' }, { weight: 10, to: 'bold' }] },
control: { type: 'variant', variant: 'control' },
bold: { type: 'variant', variant: 'bold' },
},
},
}
defineConfig validates the flow: node references, supported fields and operators, split weights, and declared variant names are checked. Design each path to end in a variant or skip; avoid cycles.
Where it runs
Everywhere the assignment runs, with the same answer:
- the React SDK (browser context: device, browser, path, query, cookies, time; country from the cloud's
ctxwhen in cloud mode); - the Next.js middleware (request headers and Cloudflare/Vercel geo);
- the drop-in script;
- server code using
assign(config, experimentId, visitorId, { context }).
Pass supported context fields with createClient(config, { context: { country: 'DE' } }) or the provider's context prop. For a plan stored in a cookie, use { field: 'cookie', key: 'plan', op: 'is', value: 'pro' }.
When to use it
- Roll a change out to one market first.
- Keep a mobile-only variant out of desktop traffic.
- Test only paid traffic (
utm_medium is cpc). - Exclude employees by cookie.
Remember the statistics: targeting narrows the sample. The verdict is about the visitors who entered, not about everyone.