Tracker & snippet
Everything the Trafnova tracker (t.js) does and every attribute you can
set on the install tag. One static file, no cookies, no consent banner required by
default - but every knob you need for SPAs, custom events, content dimensions,
privacy gating, and keeping staging traffic out is here.
Install
Paste this into the <head> of every page you want to track:
<script>window.tn=window.tn||function(){(window.tn.q=window.tn.q||[]).push(arguments)}</script>
<script defer data-token="YOUR_SITE_TOKEN" src="https://cdn.trafnova.com/t.js"></script>
Your token is on the site's Install card in the dashboard. The first line
is a tiny stub so any tn() call you make before the deferred tracker
finishes loading is buffered and replayed (nothing lost on early events). The script is
served first-party from the CDN (or your own CNAME), respects
navigator.doNotTrack, and ships a stable v: "x.y.z" version on
every payload so you can spot a stale cache.
What is tracked automatically
With just the snippet above (no extra config) the tracker collects:
- Pageviews - on load and on every SPA navigation (
pushState/replaceState/popstate/hashchange/ bfcache restore). - Engagement time - active-visible milliseconds per pageview, shipped on tab hide / close.
- Outbound link clicks -
Outbound Link: Clickwith the destination URL. - File downloads -
File Downloadfor pdf / zip / csv / docx and friends, or any link with adownloadattribute. - Form submits -
Form: Submitwith the form id / method / sanitized action path. - Page title, screen width, browser language - for content, device-class, and locale breakdowns.
POST /api/v1/event yourself. The collector is an
implementation detail of t.js (cookieless session id, dwell beacons, batched
queue, bot signals). Use the snippet and the tn() API below.
Custom events
The tracker exposes a global tn() function once loaded:
// fire a custom event with optional properties (<= 2KB JSON)
tn('event', 'Signup', { plan: 'pro', source: 'hero' });
// manually trigger a pageview - escape hatch for SPA routers that
// don't go through history.pushState
tn('pageview');
Custom events show up in the dashboard's Custom event properties breakdown.
Declarative event tagging
No JavaScript required - tag any element and the tracker fires the event on click:
<button data-tn-event="Signup" data-tn-event-plan="pro">Sign up</button>
data-tn-event is the event name; every data-tn-event-<key> attribute
becomes a property (dashes kept: data-tn-event-plan-type → plan-type).
A click on a child element still finds the tagged ancestor. If the tagged element is also an
outbound link or download, its URL (and file extension) is folded into the event's props instead
of firing a second event - so a tagged "Buy" button that points at Stripe is one
conversion, not two.
Pageview dimensions (custom properties)
Segment your content by author, category, plan, anything - declare
<meta> tags and they attach to the pageview:
<meta name="tn:author" content="Jane Doe">
<meta name="tn:category" content="Engineering">
Every name="tn:KEY" meta becomes a property {KEY: content} on the
pageview. They are re-read on each SPA navigation, so a router that swaps the meta tags per
route reports the right dimensions. These surface in the dashboard's
Custom dimensions card.
Restrict tracking to specific hostnames
Running the same snippet on staging or localhost? Keep that traffic out of your stats with an allowlist:
<script defer data-token="YOUR_SITE_TOKEN"
data-domains="example.com,www.example.com"
src="https://cdn.trafnova.com/t.js"></script>
When data-domains is present the tracker only runs if the current
location.hostname is in the comma list (case-insensitive). Absent = track
everywhere (default).
Exclude specific paths
Drop pageviews and events on admin / checkout / internal routes:
<script defer data-token="YOUR_SITE_TOKEN"
data-exclude="/admin/**, /checkout"
src="https://cdn.trafnova.com/t.js"></script>
Glob patterns: * matches within a single path segment, ** matches
across segments. So /admin/** excludes /admin/users/42, while
/blog/* excludes /blog/post but not /blog/2026/post.
Re-evaluated on each navigation, so an excluded route in a SPA is dropped while the rest of the
app is tracked.
Manual-only mode
Want full control? Turn off automatic collection and fire everything yourself with
tn():
<script defer data-token="YOUR_SITE_TOKEN"
data-auto-track="false"
src="https://cdn.trafnova.com/t.js"></script>
data-auto-track="false" loads the tracker and keeps the tn() API but
wires none of the automatic collectors (initial pageview, SPA hooks, outbound / download / form /
tagging clicks). You call tn('pageview') and tn('event', ...) when you
want.
Privacy / consent hook
Gate sending on cookie consent, or scrub PII out of payloads, with a function the tracker calls before every send:
<script defer data-token="YOUR_SITE_TOKEN"
data-before-send="tnBeforeSend"
src="https://cdn.trafnova.com/t.js"></script>
<script>
window.tnBeforeSend = function (payload) {
if (!window.userAcceptedAnalytics) return null; // consent gate: drop the event
delete payload.referrer; // PII scrub: strip a field
return payload; // send the (mutated) payload
};
</script>
window.<fnName>(payload) is called before each send, including the engagement
beacon. Return the (optionally mutated) payload to send it, or a falsy value to cancel. A hook
that throws is treated as pass-through, so a bug can't silently kill all your analytics.
Excluding yourself & specific forms
Keep your own visits out of the stats without touching the server - run this once in your
browser console, or visit any page with ?tn_ignore=true:
localStorage.setItem('tn_ignore', 'true'); // sticky, this device only
// or visit: https://example.com/?tn_ignore=true (?tn_ignore=false clears it)
To stop auto-tracking a single form's submission, add data-tn-skip to it:
<form data-tn-skip> ... </form>
Attribute reference
| Attribute | On | What it does |
|---|---|---|
data-token | script | Your site token (required). |
data-domains | script | Comma list of hostnames to track on. Absent = everywhere. |
data-exclude | script | Comma list of path globs (* / **) to drop. |
data-auto-track | script | "false" disables all automatic collection (manual tn() only). |
data-before-send | script | Name of a window function called before each send; mutate or cancel. |
data-tn-event | any element | Fires a named custom event on click; data-tn-event-* become props. |
data-tn-skip | form | Skip auto-tracking this form's submit. |
name="tn:KEY" | meta | Attaches {KEY: content} as a pageview dimension. |
Prefer the HTTP API for pulling data back out? See the API reference. Missing something? Tell us.