@getfluxly/browser

Browser SDK

Drop-in analytics for any browser page. Captures page views, clicks, forms, page leave, rage clicks, and unhandled errors out of the box, and exposes explicit track, identify, and page calls through the same transport.

Install

Bundled apps (recommended)

npm install @getfluxly/browser
import { initGFlux } from "@getfluxly/browser";

const gflux = initGFlux({
  apiKey: "gflux_pub_live_xxx",
  apiHost: "https://api.getfluxly.com",
});

gflux?.page();

Side-effect boot

For when you want the SDK to read window.__GFLUX__ and boot itself without an explicit initGFlux call.

window.__GFLUX__ = {
  apiKey: "gflux_pub_live_xxx",
  apiHost: "https://api.getfluxly.com",
};

import "@getfluxly/browser/auto";

Script tag

Always pin a specific version and supply an SRI hash so a CDN compromise cannot inject code into your page. The integrity value below is a placeholder. Copy the real hash from the GitHub release notes for the version you pin.

<script>
  window.__GFLUX__ = {
    apiKey: "gflux_pub_live_xxx",
    apiHost: "https://api.getfluxly.com"
  };
</script>
<script
  src="https://cdn.jsdelivr.net/npm/@getfluxly/browser@0.5.1/dist/gflux.iife.js"
  integrity="sha384-PLACEHOLDER"
  crossorigin="anonymous">
</script>

To generate the SRI hash yourself for any version:

curl -sL https://cdn.jsdelivr.net/npm/@getfluxly/browser@0.5.1/dist/gflux.iife.js \
  | openssl dgst -sha384 -binary | openssl base64 -A

Script tag without inline config (strict CSP)

Use data attributes when a strict CSP blocks inline scripts entirely.

<script
  src="https://cdn.jsdelivr.net/npm/@getfluxly/browser@0.5.1/dist/gflux.iife.js"
  integrity="sha384-PLACEHOLDER"
  crossorigin="anonymous"
  data-project="gflux_pub_live_xxx"
  data-host="https://api.getfluxly.com">
</script>

Pre-boot queue

The /auto and CDN builds install window.gflux immediately. Calls made before DOMContentLoaded are queued and replayed in order after the real client boots.

Queued and replayed:

Control calls take priority over old queued work:

Track an event

gflux.track("checkout_completed", {
  order_id: "ord_456",
  total_usd: 149.99,
});

track never throws. Errors go to the configured logger and are reflected in gflux.__debug() drop counters.

Identify a user

gflux.identify("user_42", {
  email: "jane@example.com",
  plan: "pro",
});

identify stitches earlier anonymous activity to the known user. Events buffered before identify are replayed with the new external_id and replayed: true when consent allows replay.

In implicit consent mode, identify also grants consent. In explicit consent mode, identify stores the user id, but buffered events wait until optIn().

Call reset() on logout or account switch before identifying another user.

identify never throws. Errors follow the same logger and drop-counter path as track.

Consent

gflux.optIn();   // user accepted tracking; flush buffered events
gflux.optOut();  // persist opt-out; drop buffer; block future events

The SDK resolves every event through this priority order:

  1. Explicit opt-out blocks.
  2. GPC blocks when gpcMode is "honor". For region: "us-ca" the SDK pins the effective mode to "honor" regardless of config; a local gpcMode: "ignore" cannot turn off GPC for California visitors.
  3. Explicit opt-in tracks.
  4. Explicit consent mode buffers until opt-in.
  5. Honored DNT buffers until opt-in or identify.
  6. Otherwise track.

Buffered events stay in memory only. They are not written to storage. When replayed, they are sent with replayed: true.

When remote config is enabled and consent is still auto, the SDK buffers early events until /v1/sdk-config returns or times out. This keeps the first page view and queued calls on the same consent decision as later events.

Autocapture

Autocapture events are fired automatically. All fire through the same transport as track.

EventTriggerMain properties
page_viewInitial load and SPA navigationurl, path, title, referrer, referrer_domain, utm
autocapture_clickClicks on links, buttons, [role="button"], inputs, or [data-gf]tag, text, href, id, classes, selector, path, data_gf
autocapture_formForm submitform_id, form_name, action, method, field_count, path
page_leavevisibilitychange hidden, beforeunload, and SPA navigationpath, time_on_page_ms
rage_click3+ clicks within 1 second on the same interactive elementtag, text, href, id, classes, selector, path, click_count, window_ms
js_errorUncaught browser errorsource, message, name, filename, lineno, colno, stack, path
unhandled_rejectionUnhandled promise rejectionmessage, name, stack, path

Disable individual capture types:

initGFlux({
  apiKey: "gflux_pub_live_xxx",
  apiHost: "https://api.getfluxly.com",
  autocapture: {
    pageviews: true,
    clicks: true,
    forms: false,
    pageLeave: false,
    rageClicks: false,
    errors: true,
  },
});

Disable all autocapture:

initGFlux({
  apiKey: "gflux_pub_live_xxx",
  apiHost: "https://api.getfluxly.com",
  autocapture: false,
});

DOM controls

Ignore a subtree

Either attribute stops autocapture and declarative capture inside the node.

<div data-gf-ignore>
  <button>Not captured</button>
</div>
<form data-gflux-ignore>
  <!-- not captured -->
</form>

Declarative events

Attach a custom event directly in HTML without writing JavaScript.

<button
  data-gflux-event="checkout_started"
  data-gflux-prop-plan="pro"
  data-gflux-prop-items="2">
  Checkout
</button>

Supported aliases:

Property values are parsed as booleans, numbers, null, JSON objects, JSON arrays, or strings. Form field values are not captured.

Configuration

KeyTypeDefaultNotes
apiKeystringrequiredBrowser publishable key (gflux_pub_...).
apiHoststringcurrent originUse https://api.getfluxly.com for hosted GetFluxly. Validated with new URL() at boot.
autocapturefalse | objectall onPer-type toggles or false for all off.
samplingnumber100Percentage of anonymous events to keep. Identified events are never sampled.
dntMode"honor" | "ignore" | "auto""auto""auto" resolves from remote config, then falls back to "ignore".
gpcMode"honor" | "ignore""honor"GPC should stay honored in production. Overridden to "honor" whenever region resolves to "us-ca".
consentMode"implicit" | "explicit" | "auto""auto""auto" uses explicit consent for EU visitors.
region"auto" | "eu" | "us-ca" | "us" | "other""auto"Usually fetched from /v1/sdk-config. "us-ca" is treated as CCPA scope and pins gpcMode to "honor" regardless of config.
remoteConfigboolean | stringtrueFetch project runtime config at boot.
bufferMaxEventsnumber200Max in-memory buffered events (FIFO eviction).
maskedSelectorsstring[][]Text under matching selectors is masked during capture. Selectors are validated at boot; invalid ones are dropped.
consentBannerobjectdefault copyBuilt-in explicit-consent banner copy. Supports a nonce field for strict CSP (see below).
maxRetriesnumber3Transport retries for transient failures (408, 425, 429, 5xx, network errors). 4xx client errors are not retried.
loggerLogger | booleanconsolePass false to silence, or supply a custom logger.
flushQueueSizenumber10Flush when the queue reaches this size.
flushIntervalMsnumber3000Background flush interval in milliseconds.

Custom logger

initGFlux({
  apiKey: "gflux_pub_live_xxx",
  apiHost: "https://api.getfluxly.com",
  logger: {
    warn: (msg, ctx) => myLogger.warn({ msg, ...ctx }),
  },
});

Pass logger: false to silence all SDK output.

CSP nonce for the consent banner

The built-in consent banner injects a <style> tag, which requires 'unsafe-inline' in style-src by default. Pass a nonce to avoid that:

initGFlux({
  apiKey: "gflux_pub_live_xxx",
  apiHost: "https://api.getfluxly.com",
  consentBanner: {
    nonce: "REQUEST_CSP_NONCE",
  },
});

Or set a meta tag once per page; the SDK reads it if no explicit nonce is passed:

<meta name="gflux-csp-nonce" content="REQUEST_CSP_NONCE">

Storage

KeyPurpose
gf_anon_idAnonymous visitor id (localStorage primary, cookie fallback with Secure on HTTPS).
gf_external_idLast identified user id.
gf_traitsLast identify traits.
gf_consentExplicit opt-in.
gf_optoutExplicit opt-out.

Lifecycle

gflux.reset()

Clear the anonymous id, identified user, traits, and consent state. Call this on logout or before identifying a different user.

gflux.reset();

gflux.destroy()

Tear down listeners and flush in-flight requests. Returns a promise that resolves once the transport has drained.

await gflux.destroy();

Await this when you need delivery guarantees for in-flight events before tearing down the host page (for example, before navigating in a single-page test harness).

gflux.__debug()

Return the current buffer size, dropped-event counters, resolver config, and current consent decision. Use when investigating why an event did not arrive. Drop counts surface sampling, autocapture toggles, and consent rejections.

gflux.__debug();
// {
//   bufferSize: 3,
//   bufferDropped: 0,
//   dropCounts: { sampled: 12 },
//   resolverConfig: { dntMode, gpcMode, consentMode, region },
//   decision: { decision: "track" }
// }

Errors

track and identify never throw. Errors are:

This means a mis-typed event name or a network failure will never crash your application.

Bundle size

The IIFE bundle gzips to ~9 KB. Tree-shaken ESM imports are smaller.