Browser SDK
The core entry of @leadmaps/tracking exposes everything you need for
pageview, custom events, identification, consent, and identity reads. The
auto-capture features (Web Vitals, errors, clicks, scroll depth, engaged time,
and Experience) live behind lazy modules or their own
subpaths.
leadmaps is the analytics service that receives these events. Your visitors’ data goes to a store you control, in EU-resident infrastructure by default.
Initialise
Section titled “Initialise”import { init } from '@leadmaps/tracking';
init({ apiKey: 'YOUR_API_KEY', siteId: '11111111-1111-4111-8111-111111111111', host: 'https://collect.leadmaps.nl', // optional: mode: 'cookied', autoCapture: true, sessionTimeout: 30 * 60 * 1000, respectDnt: true, defaultConsent: 'unknown', experience: true, sampling: { mousemove: 0.1 },});InitOptions
Section titled “InitOptions”| Field | Type | Default | Notes |
|---|---|---|---|
apiKey | string | required | Ingest key from your leadmaps workspace. |
siteId | string | required on hosted leadmaps | Site UUID sent as X-Site-Id. |
host | string | required | Collector origin (scheme + host). Throws on empty. |
sessionTimeout | number | 30 * 60 * 1000 | ms. Negative / non-finite values clamp silently. |
respectDnt | boolean | true | When true, a DNT-blocked init never installs listeners. |
mode | 'cookied' | 'cookieless' | 'cookieless' | Cookieless sets no cookie or localStorage identity. Cookied identity is explicit opt-in. |
privacy | 'anonymous' | 'consent' | 'consent' | Anonymous mode performs no browser storage or fingerprinting and skips replay, auto-capture, and feature flags. |
defaultConsent | 'unknown' | 'granted' | 'unknown' | Pass 'granted' if you obtained consent before bootstrap. |
sampling | Record<string, number> | {} | Per-event-type rates in [0, 1]. |
experience | boolean | ExperienceOptions | remote site setting | Consent-gated, lazy interaction summaries. false always disables it. See Experience. |
Calling init more than once is a no-op for the second call onward.
Identity and privacy defaults
Section titled “Identity and privacy defaults”Omit mode to use cookieless identity. The SDK derives a daily-rotating
anonymous id from coarse browser characteristics. It sets no cookie and writes
no identity to localStorage. A session id may use tab-scoped sessionStorage
to keep navigation within one tab connected.
Set mode: 'cookied' only when you deliberately need a persistent
first-party identifier across days. That mode writes the _strk_aid cookie
with a localStorage backup and stores session and identified-user continuity
in localStorage, but only after consent. Use it when the same consenting
browser should remain one Unique visitor across return visits.
Cookieless and anonymous identifiers rotate daily. In a range spanning more than one UTC day, the same browser can therefore contribute more than one Unique visitor. Unique visitors is always an estimate of distinct browsers, not a verified count of people.
Cookieless identity is not the same as anonymous privacy mode. A site using
privacy: 'anonymous' performs no browser storage or fingerprinting, and the
collector derives a daily aggregate key server-side. See
Consent gating before choosing a mode. Cookieless identity
does not by itself remove consent requirements.
Track custom events
Section titled “Track custom events”import { track } from '@leadmaps/tracking';
track('checkout_completed', { order_id: 'ord_42', total_cents: 4900, currency: 'USD',});name becomes the wire type discriminator. props is sanitized (string
clamp, function / symbol drop) before queuing. Empty name is dropped
silently. Calls before init are dropped.
For type-safe track() calls narrowed to your tracking plan, see
TypeScript codegen.
Identify users
Section titled “Identify users”import { identify } from '@leadmaps/tracking';
identify('user_42', { plan: 'pro' });Binds the current anon_id to a known user_id. If a different user_id
was previously stored, leadmaps emits a merge event before the new
identify so cross-device timelines stay consistent. Empty / non-string
userId produces a console.warn and is otherwise a no-op (the SDK never
throws from public surfaces inside the host page).
Pageviews
Section titled “Pageviews”init() records the first pageview automatically and listens for SPA
navigation (pushState / replaceState / popstate). You do not need to
call pageview() manually in 99 % of apps. If your router uses a non-history
mechanism, call captureContext(url) followed by your custom emit.
Consent
Section titled “Consent”import { grantConsent, revokeConsent, getConsentState } from '@leadmaps/tracking';
grantConsent(); // optional: pass a token attached as X-Consent on every requestrevokeConsent(); // purges the in-memory queue immediatelygetConsentState(); // 'unknown' | 'granted' | 'revoked'While consent is 'unknown' (the default), events queue in memory but no
network request goes out. grantConsent() drains the queued events.
revokeConsent() empties the queue and makes every subsequent send a no-op
for the rest of the page lifetime.
See Consent gating for the server-side enforcement.
Identity reads
Section titled “Identity reads”import { getUserId, getAnonId, getAnonIdAsync, getSessionId } from '@leadmaps/tracking';
getUserId(); // string | null — null if identify() never rangetAnonId(); // string — UUID in cookied mode, daily fingerprint in cookieless modeawait getAnonIdAsync(); // waits for the stable cookied id before returninggetSessionId(); // string — mints a new session id if the inactivity window has lapsedgetSessionId() extends lastActiveTs as a side effect. In the cookieless
default, the session id may use tab-scoped sessionStorage. Cookied mode uses
localStorage for cross-tab persistence.
Use getAnonIdAsync() when starting an optional module that must bind to the
same persistent id as queued events. Regular tracking calls handle this wait
automatically.
Live presence
Section titled “Live presence”Consented installs with auto-capture send a small page-presence signal for the active visitor count. A visible tab counts as active, closing or hiding the last tab clears it, and an automatic expiry covers browsers that suppress a close signal. Multiple tabs from one browser still count once. Presence is not stored as an analytics event and does not consume the event quota.
Anonymous mode does not send presence. Older SDKs and installs using a custom event proxy keep the recent-event fallback until they are upgraded or their proxy adds the presence route.
Context
Section titled “Context”import { captureContext, getCurrentContext } from '@leadmaps/tracking';
captureContext('https://example.com/pricing');// returns: { utm, referrer, viewport, screen, language, timezone, ua }
getCurrentContext(); // EventContext | nullcaptureContext is called automatically on every pageview; you only need to
call it manually if you are wiring your own custom navigation hook.
import type { InitOptions, TrackingEvent, ConsentState,} from '@leadmaps/tracking';TrackingEvent is exported only for type narrowing in adjacent modules
(e.g. the errors and engaged-time subpath signatures). Customers do not
construct TrackingEvent instances directly.
What’s NOT public
Section titled “What’s NOT public”The SDK exports send, getConfig, and getQueueForTests from the core
entry for use by the opt-in subpaths. They are not part of the contract, so do
not import them in production code. Test seams (__resetForTests,
__resetConsentForTests, __resetIdentifyForTests, etc.) are private.