Web SDK reference
@narrato-app/sdk-web is the browser SDK: track events, identify users, auto-capture
page views. Zero runtime dependencies, ~3.8 KB gzipped, shipped as ESM, CJS, and a
script-tag (IIFE) build with TypeScript declarations.
- Offline-safe: events persist in
localStorageand survive page loads. - Batched delivery with retry + exponential backoff;
sendBeaconon page hide so unload doesn’t lose events. - Sessions with 30-minute inactivity renewal.
- SSR-safe: outside a DOM it falls back to in-memory storage and no-ops browser hooks, so it’s safe to import in server-rendered apps.
Install
Section titled “Install”npm install @narrato-app/sdk-webimport * as narrato from '@narrato-app/sdk-web';
narrato.init('<your-write-key>', { host: '<your-api-url>' });narrato.track('signup_completed', { plan: 'free' });No bundler? The IIFE build exposes a global narrato with the same API:
<script src="https://unpkg.com/@narrato-app/sdk-web@0.1.3/dist/index.global.js" integrity="sha384-VfaFigAs9+sz2beCxFuZXSgi6ajbUl6ElD/6kXZhyYTmURyRGY9DTXTGWKWB5/VW" crossorigin="anonymous"></script><script> narrato.init('<your-write-key>', { host: '<your-api-url>' });</script>| Function | Description |
|---|---|
init(writeKey, options?) |
Initialize the shared client. Call once, early. Returns the client. |
track(eventName, properties?) |
Queue a custom event. |
identify(userId, traits?) |
Associate subsequent events with a known user; emits an identify event carrying the traits. |
page(properties?) |
Track a page view. URL, path, title, referrer, and the session’s initial_referrer are captured automatically. |
reset() |
Log-out: new anonymous id, new session. |
flush() |
Send everything queued now (returns a promise). |
shutdown() |
Remove timers/listeners (tests, hot reload). Queued events stay persisted. |
Need multiple isolated clients (rare)? new NarratoClient({ writeKey, … }) from the
same package.
Options (init second argument)
Section titled “Options (init second argument)”| Option | Default | Description |
|---|---|---|
host |
https://api.narrato.app |
Narrato API base URL (no trailing slash). |
flushAt |
20 |
Flush as soon as this many events are queued. |
flushInterval |
10000 |
Also flush on this interval (ms). |
sessionTimeout |
1800000 |
Session renews after this much inactivity (ms). |
autoPageviews |
true |
Track initial load + SPA navigations (pushState/replaceState/popstate). |
maxQueueSize |
1000 |
Offline buffer cap; oldest events are dropped beyond it. |
appVersion |
"" |
Reported as app_version on every event. |
debug |
false |
Log SDK activity to the console. |
How it behaves
Section titled “How it behaves”Identity. A random anonymous id is generated on first load and persisted. It’s
sent as user_id until identify() supplies a real one, and always as device_id,
so pre- and post-login activity can be joined. reset() rotates it.
Sessions. A session id persists across page loads and renews after
sessionTimeout of inactivity. Every tracked event slides the window.
Delivery. Events queue locally and flush when flushAt is reached or every
flushInterval, in batches of up to 500. Network errors, 429s, and 5xxs keep
events queued and back off exponentially (1s → 30s cap); other 4xx responses drop
the batch (retrying can’t fix a rejected payload). On pagehide/tab-hidden the
queue is handed to navigator.sendBeacon, which survives page unload.
The write key travels as the X-Narrato-Write-Key header, and as a
?writeKey= query parameter on beacon requests, which can’t set headers. The API
validates it and stamps every event with the owning project server-side. An invalid
key gets a 401 and the batch is dropped; turn on debug: true and check the
browser console if events aren’t arriving.
Auto-capture scope. Only page views are auto-captured. Clicks and form submissions are deliberately not auto-tracked: they generate noisy data and can capture sensitive input without consent controls. Track what matters explicitly, and see Privacy & PII before attaching user data.
Quota signals. When your workspace approaches its monthly event quota, ingestion
responses carry X-Narrato-Usage-Warning / X-Narrato-Usage-Percent headers, and
over-quota Free workspaces eventually receive 429s. The SDK treats those as
retryable, so events queue rather than vanish. Details in
Limits & billing.
Invalid events are dropped one at a time. track() checks each event against the
API’s rules (event names: letters, digits, _, - and spaces; properties: at most 100
keys, 5 levels deep, 32 K characters of JSON, JSON-serializable). An event that breaks them is not queued,
and a [narrato] warning in the console says why. It never takes other events down with it.
Page views redact credentials. Query and fragment parameters that look like secrets
(token, access_token, key, password, …, and code/state on OAuth callbacks) are recorded as
[redacted] in page_url, search and referrer. Hash routes like #/settings are kept,
and a page view is recorded only when the URL actually changes.
Single-page apps. On an in-app navigation the referrer is the page being left (the
browser’s document.referrer only describes how the visit arrived, so the first page view
keeps it). Routers update the URL before they render the new page, so the page view waits
up to 2 seconds for the new <title>; its URL and timestamp are still the navigation’s.
A page whose title never changes is sent with it. If another navigation, or leaving the page,
comes first, the page view is sent without a title rather than with the wrong one.
Every page view also carries initial_referrer: the referrer the session arrived with, the
same for each page of the visit, so traffic sources can be counted per session. It’s empty for
direct visits, and for a session that starts from a page of your own site (after a logout, or
a visit idle past sessionTimeout).
Known limitation. The offline queue is shared per-origin via localStorage, so
no tab loses another’s unsent events. Two tabs flushing at the same instant can, in rare interleavings, duplicate a batch (or, rarer still, drop one tab’s write).
Fine for analytics; flagged here for honesty.