Vanilla Breeze

Analytics

Vanilla Breeze ships with a small, cookieless, privacy-first analytics runtime. Pages fire a page.view on load; components dispatch typed events through Analytics.track(); a Cloudflare Pages Function writes aggregates to D1. The dashboard at /stats/ renders the results.

Configuration

Set window.vbAnalyticsConfig before vanilla-breeze-core.js loads. The runtime reads it once at init.

Options

KeyDefaultPurpose
siteId 'default' Written to the site_id column on every row; lets one D1 database serve multiple sites.
transport 'disabled' 'beacon' (POST to the endpoint), 'console' (log to devtools — useful in dev), or 'disabled' (no-op).
endpoint '/api/analytics' Base URL; individual paths are ${endpoint}/hit, /click, /events.
sampleRate 1 Fraction of events to record. See Sample rate.
consent null Function returning a boolean. If present and returns false, no events fire. Evaluated every time, so you can flip consent without reloading.
urlMasks null Array of { pattern, replace }. Sensitive path segments (user IDs, tokens) are rewritten before the beacon is sent.
allowInIframe false By default, Analytics detects when it's running inside an iframe (<browser-window> demos, third-party embeds) and disables itself to avoid spamming events. Set true to override.

Sample rate

The sampleRate option trades ingest volume for dashboard accuracy. At small scale (single-digit visits per day) leave it at 1 — recording everything gives the best signal. At ≥10,000 events per day you may want to sample down to keep D1 storage and query latency predictable.

sampleRate works per event:

So sampleRate: 0.1 means every event has an independent 10 % chance of firing — page views, clicks, form submits, Web Vitals, errors, scroll, attention, everything.

Typical settings

VolumeRecommendationWhy
< 1,000 events / day sampleRate: 1 Record everything. Storage is tiny, and low-volume data is already noisy enough.
1,000 – 100,000 / day sampleRate: 1 (still) D1 writes are cheap; query cost at this scale is negligible. Only sample if you hit a specific bottleneck.
100,000 – 1M / day sampleRate: 0.25 to 0.1 Reduces D1 row count 4–10×. Statistical precision on top-10 queries stays high; rare events lose resolution.
> 1M / day sampleRate: 0.01 or move to Analytics Engine At this scale a single D1 DB fills up fast. Consider splitting or swapping the transport to write-only Cloudflare Analytics Engine instead of row-per-event D1.

Reading the dashboard with sampling on

When sampleRate < 1, every count on /stats/ under-reports by the sampling factor. For a population estimate:

Masking is one-way and runs in the browser; there is no "unmask" step at query time. If you later want the unredacted path, you need to add it to props explicitly via Analytics.track().

Transport choices

beacon (production)

Uses navigator.sendBeacon() with a fetch(..., { keepalive: true }) fallback. Beacons survive page unload and never block navigation. This is the only transport that produces server-side data; use it once your ingest endpoints are live.

console (dev visibility)

Logs each event as [vb:analytics] hit {...} with a styled prefix. No network requests are made — useful for confirming events fire as expected before pointing at a real endpoint.

disabled (explicit opt-out)

Every call to Analytics.track() is a no-op. Useful for local scripts, ephemeral demos, or subdomains where analytics aren't wanted.

Opt-out precedence

Before every event fires, the runtime checks these signals in order. Any one returning "opted out" drops the event:

  1. sessionStorage.getItem('vb_optout') === '1' — written by <analytics-panel>'s Pause button or Analytics.setConsent(false).
  2. navigator.globalPrivacyControl === true — browser-level GPC signal.
  3. navigator.doNotTrack === '1' — legacy DNT header.
  4. The consent callback you supplied, if any.
  5. data-vb-no-track on <html> — opts the whole page out.

Per-element exclusion via data-vb-no-track on any ancestor also drops data-vb-event firings from inside that subtree.

Seeing the data

The stats dashboard renders totals, top pages, top events, referrers, countries, Web Vitals, engagement, and recent errors. All panels are scoped to the site query parameter (defaults to vb-docs), with time windows of 24 hours, 7 days, or 30 days.

If you've turned sampling on, remember to divide by sampleRate to estimate totals.

Related