Skip to main content

Lightweight, privacy-first browser analytics — setup, configuration, data attributes, and collection control

Last updated September 14, 2026

Browser Analytics

Flaggr includes a lightweight browser analytics script that captures Web Vitals, page views, and user interactions — and automatically correlates them with active feature flag variants. This lets you measure real-world performance impact of every flag change.

The script is under 5KB gzipped, uses no cookies, collects no PII, and respects Do Not Track by default.

Quick Start

Add the script tag to your HTML:

<script
  src="https://cdn.flaggr.dev/a.js"
  data-site-id="proj_abc123"
  defer
></script>

That's it. The script automatically collects:

  • Web Vitals — LCP, CLS, INP, TTFB, FCP
  • Page views — initial load and SPA navigations
  • Sessions — anonymous session tracking via sessionStorage
  • Flag variants — auto-detected from Flaggr SDK globals

Events are batched and sent to https://ingest.flaggr.dev/e via fetch with keepalive, falling back to navigator.sendBeacon on page unload.

Configuration

All configuration is via data-* attributes on the script tag. No JavaScript API is needed.

Required attributes

AttributeDescription
data-site-idYour project ID. Used to associate events with your Flaggr project.

Optional attributes

AttributeDefaultDescription
data-endpointhttps://ingest.flaggr.dev/eCustom analytics ingestion endpoint
data-track-clicksfalseEnable click tracking on interactive elements
data-track-errorsfalseEnable JavaScript error tracking
data-track-scrollfalseEnable scroll depth milestone tracking
data-respect-dnttrueRespect the browser's Do Not Track setting
data-debugfalseEnable [flaggr:analytics] console logging

Full example

<script
  src="https://cdn.flaggr.dev/a.js"
  data-site-id="proj_abc123"
  data-endpoint="https://ingest.flaggr.dev/e"
  data-track-clicks="true"
  data-track-errors="true"
  data-track-scroll="false"
  data-respect-dnt="true"
  data-debug="false"
  defer
></script>

Event Types

Page views (always on)

Captured on initial page load and SPA navigations (History API). Each event includes:

  • path — URL path
  • title — Document title
  • referrer — Referring page
  • viewportWidth, viewportHeight — Browser viewport dimensions
  • connectionType — Network connection type (if available)
  • renderServerMs, renderNetworkMs, renderTotalMs — Backend-aware timing derived from the document navigation entry or recent same-origin API responses
  • renderServerTimings — Parsed Server-Timing spans for the page load or correlated backend requests
  • renderBackendRequests — Recent same-origin /api/* responses that contributed to an SPA navigation, including status, duration, request ID, and parsed server timing spans

Web Vitals (always on)

Collected via PerformanceObserver. Each vital includes a numeric value and a rating:

MetricGoodNeeds ImprovementPoor
LCP≤ 2500ms≤ 4000ms> 4000ms
CLS≤ 0.1≤ 0.25> 0.25
INP≤ 200ms≤ 500ms> 500ms
TTFB≤ 800ms≤ 1800ms> 1800ms
FCP≤ 1800ms≤ 3000ms> 3000ms

Click tracking (opt-in)

When data-track-clicks="true", captures clicks on interactive elements:

  • selector — CSS selector of the clicked element
  • tag — HTML tag name
  • text — Button/link text (truncated to 100 characters)
  • trackId — Value of data-track attribute, if set on the element
  • href — Link URL, if applicable

Use data-track="signup-cta" on elements to give them stable identifiers for analytics.

Error tracking (opt-in)

When data-track-errors="true", captures unhandled JavaScript errors and promise rejections:

  • message — Error message
  • source — Source file
  • line, column — Error location
  • stack — Stack trace (truncated to 500 characters)

Scroll depth (opt-in)

When data-track-scroll="true", captures scroll depth milestones at 25%, 50%, 75%, and 100%. Uses requestAnimationFrame throttling for minimal performance impact.

Flag Variant Correlation

The analytics script automatically detects active feature flag variants and attaches them to every event. This enables per-variant performance analysis in the Analytics Dashboard.

Detection order

  1. window.__FLAGGR_BOOTSTRAP__ — SSR-injected flag values (set by Next.js server components)
  2. window.__FLAGGR_FLAGS__ — Live SDK state (updated after client-side evaluation)
  3. flaggr:flags-changed event — Custom event dispatched when flag values change

The Flaggr Web Provider automatically populates these globals. If you use a custom provider, expose flag values on window.__FLAGGR_FLAGS__ and dispatch a flaggr:flags-changed CustomEvent with { detail: Record<string, unknown> } when values change.

Dynamic Collection Control

Collection can be controlled remotely via the _flaggr_analytics feature flag, allowing you to adjust what data is collected without redeploying.

String levels

Set the _flaggr_analytics flag to one of these string values:

LevelCollects
"off"Nothing (sampling = 0)
"minimal"Page views + Web Vitals
"standard"Page views + Web Vitals
"detailed"Page views + Web Vitals + clicks + errors
"full"Everything (page views + vitals + clicks + errors + scroll)

Object config

For fine-grained control, set _flaggr_analytics to a JSON object:

{
  "pageviews": true,
  "vitals": true,
  "clicks": false,
  "errors": false,
  "scroll": false,
  "sampling": 1.0
}

Sampling

The sampling field controls probabilistic collection:

  • 0 — Block all events
  • 1 — Collect all events (default)
  • 0.5 — Collect from 50% of sessions

Sampling is decided once per session and remains stable until the page is reloaded.

Elevation rules

The data-* attributes on the script tag define a static floor. The _flaggr_analytics flag can only elevate collection above this floor, never reduce below it. For example, if data-track-clicks="true" is set, clicks will always be collected regardless of the flag value.

Transport & Delivery

Events are queued in memory and flushed:

  • Every 5 seconds (periodic flush)
  • When 20 events accumulate (queue size limit)
  • On page unload via navigator.sendBeacon()

Failed requests are retried once with a 1-second backoff. The transport uses fetch with keepalive: true for reliability during page transitions.

Privacy

The analytics script is designed to be privacy-friendly by default:

  • No cookies — Session IDs use sessionStorage only (scoped to tab, cleared on close)
  • No PII — Never collects user IDs, emails, names, or IP addresses
  • No third-party sharing — All data stays in your project's database
  • Do Not Track — Respects navigator.doNotTrack by default (opt out with data-respect-dnt="false")
  • Country-level geo only — Derived from Cloudflare edge headers, not IP geolocation
  • GDPR-friendly — Minimal data collection with anonymous sessions

Ingestion Pipeline

Events flow through a Cloudflare Worker at ingest.flaggr.dev/e:

  1. Validation — Payload size, event count, required fields
  2. Authentication — Site ID verified against project registry
  3. Enrichment — Country, browser, OS, device from request headers
  4. Session upsert — Page count incremented, duration calculated, flags merged
  5. Bulk insert — Valid events written to analytics_events table

The worker responds with 202 Accepted and returns { accepted: N, rejected: M }.

Backend Performance and SLOs

Browser analytics can now correlate render timing with backend response metadata, but it should not be your SLO source of truth by itself.

  • Use browser analytics to answer user-experience questions such as "was this slow render caused by backend work or client work?"
  • Use Flaggr's server-side Prometheus and OpenTelemetry metrics to define backend latency and availability SLOs
  • Emit meaningful Server-Timing headers from your document and API responses so the browser analytics payload can carry backend spans alongside page load data

Next Steps