Measure experiment outcomes by combining browser analytics with feature flag variants
Last updated October 25, 2026
Analytics & A/B Testing
This guide shows how Flaggr's browser analytics and feature flag system work together to measure the real-world impact of experiments. Every event captured by the analytics script includes the active flag variants, making it straightforward to compare performance, conversions, and user behavior across variants.
How It Works
The two systems meet in the page:
- Your code evaluates flags (with
@flaggr/sdkor any OpenFeature provider), and the values are published onwindow.__FLAGGR_FLAGS__ - Analytics script reads those values and attaches them to every event it sends to
https://ingest.flaggr.dev/e - API endpoints aggregate data by variant, enabling per-variant comparisons
- Dashboard visualizes variant impact on Web Vitals, conversions, and funnels
With @flaggr/sdk 0.5.0 and later there's no glue to write: in a browser the SDK publishes every value it resolves (an object flag by its variant name). With an earlier SDK or another OpenFeature provider, publish the values yourself after evaluating (step 4 shows how). Events sent without flag values can't be split by variant.
Setting Up an A/B Test with Analytics
1. Create a flag with variants
curl -X POST "https://flaggr.dev/api/flags" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"key": "checkout-redesign",
"name": "Checkout Redesign",
"type": "string",
"defaultValue": "control",
"projectId": "proj_abc123",
"serviceId": "svc-web",
"variants": [
{ "name": "control", "value": "control", "weight": 50 },
{ "name": "redesign", "value": "redesign", "weight": 50 }
]
}'2. Create an experiment
curl -X POST "https://flaggr.dev/api/projects/proj_abc123/experiments" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Checkout Redesign Test",
"flagKey": "checkout-redesign",
"serviceId": "svc-web",
"variants": [
{ "name": "control", "value": "control", "weight": 50 },
{ "name": "redesign", "value": "redesign", "weight": 50 }
]
}'3. Install both SDK and analytics script
Create a site for the web property first: in the console, open the service and choose Sites → Onboard site, or have your assistant call the MCP create_site tool. Flaggr generates the site's ID (site_…); that ID, not the project ID, goes in data-site-id. The service's Sites tab shows the ID under each site's domain, and Copy analytics tag in the row menu copies the a.js tag. Give the site the domain the pages are served from: events sent from other origins are marked untrusted, analytics views leave them out by default, and rollout guardrails ignore them (Trust marks).
<!-- Flaggr SDK (evaluates flags) -->
<script src="https://cdn.flaggr.dev/sdk.js"
data-service-id="svc-web"
data-api-key="fgr_your_read_token"
data-api-url="https://api.flaggr.dev"
data-environment="production"
defer></script>
<!-- Flaggr Analytics (captures events + correlates with flags) -->
<script src="https://cdn.flaggr.dev/a.js"
data-site-id="site_7c1e4a9b2f6d8e0a3b5c7d9f"
data-track-clicks="true"
defer></script>https://cdn.flaggr.dev/sdk.js is the @flaggr/sdk browser bundle, byte for byte the npm release (the manifest at https://cdn.flaggr.dev/manifest.json shows its version). Keep data-api-url: without it the 0.4.0 bundle sends evaluations to the page's own origin instead of https://api.flaggr.dev. Every visitor can read the SDK script's data-api-key, so use a read-only token. If your pages send a Content-Security-Policy, allow https://cdn.flaggr.dev in script-src (it serves both scripts), and https://api.flaggr.dev and https://ingest.flaggr.dev in connect-src, plus https://flaggr.dev, where the SDK sends outcomes and telemetry from 0.5.0.
4. Use the flag in your code
In React, evaluate the flag with @flaggr/sdk/react 0.5.0 or later, which publishes the value for the analytics script. 0.5.0 is the next release: until npm view @flaggr/sdk version prints it, follow With an earlier SDK or another provider instead.
import { FlaggrProvider, useStringFlag } from '@flaggr/sdk/react';
export function App() {
return (
<FlaggrProvider serviceId="svc-web" apiKey={process.env.NEXT_PUBLIC_FLAGGR_TOKEN} environment="production">
<CheckoutPage />
</FlaggrProvider>
);
}
function CheckoutPage() {
const variant = useStringFlag('checkout-redesign', 'control');
return variant === 'redesign'
? <NewCheckout />
: <OriginalCheckout />;
}From then on, every event from the page carries checkout-redesign = "redesign" (or "control"). The script-tag SDK 0.5.0 and later publishes the value from await flaggr.flag('checkout-redesign', 'control') the same way.
With an earlier SDK or another provider
@flaggr/sdk 0.4.0 and earlier (including the sdk.js bundle while the manifest shows 0.4.0) and other OpenFeature providers don't publish values. Merge each value into the map yourself after evaluating:
<script type="module">
const variant = await flaggr.flag('checkout-redesign', 'control');
window.__FLAGGR_FLAGS__ = { ...window.__FLAGGR_FLAGS__, 'checkout-redesign': variant };
window.dispatchEvent(new CustomEvent('flaggr:flags-changed', { detail: window.__FLAGGR_FLAGS__ }));
</script>In a React component, run the same two lines in a useEffect that depends on the value.
5. Track conversion events
For conversion tracking beyond page views and vitals, dispatch a flaggr:conversion event. The analytics script sends it tagged with the page's flag values:
// After a successful purchase
window.dispatchEvent(new CustomEvent('flaggr:conversion', {
detail: { name: 'purchase', value: 49.99 },
}));Measuring Results
Web Vitals by Variant
Check if the redesign affects Core Web Vitals:
curl "https://flaggr.dev/api/analytics/vitals?siteId=site_7c1e4a9b2f6d8e0a3b5c7d9f&range=7d&flagKey=checkout-redesign" \
-H "Authorization: Bearer <token>"With flagKey, vitals lists hourly rows (hour, metric, variant, p50, p75, p95, count) for events that carried the flag. Each row is one combination of the page's flag values, so when pages carry other flags a variant has several rows an hour, and percentiles from separate rows can't be combined into one. For one LCP, CLS and INP p75 per variant, use the flags × RUM report below.
Conversion Rates by Variant
Browser conversions (the flaggr:conversion events from step 5) are reported per variant by the flags × RUM report, alongside sessions, error rate and vitals:
curl "https://flaggr.dev/api/analytics/flag-rum?siteId=site_7c1e4a9b2f6d8e0a3b5c7d9f&range=7d&flagKey=checkout-redesign" \
-H "Authorization: Bearer <token>"The variants endpoint below and the funnel analysis don't read browser analytics. They count the evaluations the Flaggr control plane (https://flaggr.dev) recorded and the outcome events stored there (https://api.flaggr.dev forwards outcome events to it). With the setup in step 3 the evaluation counts stay empty, because evaluations served by https://api.flaggr.dev aren't recorded. In a browser, @flaggr/sdk 0.4.0, the version on npm now (and the sdk.js bundle while the manifest shows 0.4.0), sends client.trackOutcome() events with navigator.sendBeacon, which can't carry the credential that POST /api/events/outcomes requires, so they aren't stored. 0.5.0 (the next release) sends them in a keepalive fetch with the client's apiKey as a bearer token, to https://flaggr.dev/api/events/outcomes when the client's apiUrl is https://api.flaggr.dev (else to its apiUrl). From a page on another origin the browser sends a CORS preflight first, and https://flaggr.dev answers that preflight for /api/events/outcomes only from its next deploy: until then the browser blocks the request and the outcome isn't stored. Until 0.5.0 and that deploy are both out, record browser outcomes from your server instead, with a token that can evaluate the flag. Give each outcome a userId (the funnel counts users by it) and the variant the user saw; trackOutcome() 0.5.0 fills in the variant it last evaluated for the flag and that user when the event has none:
curl -X POST "https://flaggr.dev/api/events/outcomes" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"events": [
{ "flagKey": "checkout-redesign", "variant": "redesign", "eventName": "purchase", "eventValue": 49.99,
"userId": "user-123", "serviceId": "svc-web", "environment": "production" }
]
}'Evaluation counts come only from evaluations https://flaggr.dev served (for example to an SDK whose apiUrl is https://flaggr.dev) and from exposure events your code reports to POST /api/events/exposures/batch.
curl "https://flaggr.dev/api/analytics/flags/checkout-redesign/variants?projectId=proj_abc123&environment=production&period=7d&eventName=purchase" \
-H "Authorization: Bearer <token>"Response:
{
"flagKey": "checkout-redesign",
"variants": [
{ "variant": "control", "evaluations": 5000, "conversions": 250, "conversionRate": 0.05 },
{ "variant": "redesign", "evaluations": 5100, "conversions": 357, "conversionRate": 0.07 }
],
"overallConversionRate": 0.06
}A 40% improvement in conversion rate for the redesign variant.
Funnel Analysis
Trace the full checkout funnel by variant. Each step is the eventName of outcome events recorded as above. Users are told apart by their events' userId, and a user reaches a step only with events for it and for every step before it:
curl "https://flaggr.dev/api/analytics/funnel?projectId=proj_abc123&flagKey=checkout-redesign&steps=page_view,add_to_cart,checkout_start,purchase&environment=production&period=7d" \
-H "Authorization: Bearer <token>"This shows where each variant loses users. If redesign has less drop-off at the checkout_start → purchase step, that's direct evidence the new flow converts better.
Flag Impact in the Console
Open Console > [Project] > Analytics and choose the Flags tab. Flag impact on browser experience compares treatment and control for every flag with exposure in the window: the treatment's share, exposed sessions, and the change in LCP p75, CLS, INP and error rate against control. Each cohort needs at least 20 sessions. Select a flag to see its sessions by browser and every variant seen on browser traffic. Below, Variant and funnel analysis shows evaluation outcomes per variant and conversion funnels for the most-exposed flags.
Best Practices
Run experiments for statistical significance
Don't make decisions on small samples. Wait until each variant has enough traffic for reliable comparisons. A general rule: at least 1,000 evaluations per variant for conversion metrics, more for Web Vitals.
Monitor vitals, not just conversions
A variant might increase conversions but degrade LCP or CLS. The analytics dashboard shows both — always check performance impact alongside business metrics.
Use the _flaggr_analytics flag for sampling
For high-traffic sites, use the dynamic collection control to sample events rather than collecting everything. Set the _flaggr_analytics flag's value to:
{
"sampling": 0.1,
"vitals": true,
"clicks": true,
"errors": true
}This collects from 10% of page loads. Each page load is in or out of the sample as a whole, so every sampled page view keeps all of its events.
Export data for deeper analysis
Use the export endpoint to send evaluation and outcome data to your data warehouse for more sophisticated statistical analysis.
curl -X POST "https://flaggr.dev/api/analytics/export" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"projectId": "proj_abc123",
"destination": "webhook",
"webhookUrl": "https://your-warehouse.com/flaggr-hook",
"period": "7d"
}'Next Steps
- Browser Analytics — Install and configure the analytics script
- Analytics Dashboard — Interpret the dashboard visualizations
- Experiments & A/B Testing — Set up experiments and traffic allocation
- Analytics API Reference — Full endpoint reference