Tracking script reference
The Statable tracking script is a small JavaScript bundle you embed once on every page you want to measure. It auto-tracks pageviews, engagement, scroll depth, outbound clicks, file downloads, and exposes window.statable.t() for custom events.
This page is the canonical reference for the bundle, HTML attributes, and install patterns. For the JavaScript API see JavaScript API. For wire format see Event payload reference.
The bundle
s.js, ~2.5 KB gzipped. Auto-tracks pageviews, engagement time, scroll depth, outbound link clicks, file downloads, form submits, and exposes window.statable.t() for custom events.
Embed widgets (geo-pop, top-cities, weather, etc.) ship as separate bundles (
gp.js,gw.js,luw.js,mw.js,tcw.js). Documented under Embeds, not here.
Installation
Place the snippet in <head> with defer. defer ensures the script doesn't block HTML parsing and runs after the document is parsed.
Replace YOUR_SITE_ID with the numeric Site ID from Site settings → Tracking Code. The SDK reads the ID from the URL path, so no data-id attribute is required on paid plans.
URL structure
| Path | Used for |
|---|---|
/js/{site_id}/s.js | Standalone tracker (paid plans) |
/js/{hash}/{widget}.js | Standalone widget (paid plans) — gw, luw, mw, tcw |
/js/{hash}/t/{widget}.js | Widget + tracker bundle (Hobby plan; tracker is bundled inside the widget) |
The Hobby plan has no separate tracker — pick a widget and the install snippet is generated from /api/site/script-url.
Attributes reference
| Attribute | Required | Default | Description |
|---|---|---|---|
src | yes | (none) | URL of the bundle. Site ID is encoded in the path. |
defer | recommended | (none) | Defer script execution until HTML is parsed |
data-id | no | derived from src | Numeric Site ID. Only needed for Hobby widget bundles (/t/...) as a fallback. |
data-tracking-api | no | <script-origin>/api/event | Override the ingest endpoint (proxy / self-hosted) |
data-before-send | no | (none) | Name of a global function that mutates props before send |
data-statable-{key} | no | (none) | Sticky custom property attached to every event from this page load |
data-tracking-api (optional)
By default the SDK posts events to <script-origin>/api/event. If your script is loaded from https://statable.com/js/YOUR_SITE_ID/s.js, the endpoint is https://statable.com/api/event.
Override this if you proxy traffic through your own domain (recommended for first-party tracking, ad-blocker resilience, or self-hosted ingesters):
<script defer
src="https://statable.com/js/YOUR_SITE_ID/s.js"
data-tracking-api="https://example.com/proxy/event"></script>
The endpoint must accept POST with Content-Type: text/plain and a JSON body. Exact shape: Event payload reference.
data-before-send (optional)
Enrich, redact, or sample events before they leave the browser. The value is the name of a global function (not an inline expression). The function receives the current props object and must return the modified object.
<script>
function statableEnrich(props) {
// Attach the current user ID, plan, and feature flags
if (window.currentUser) {
props.userId = window.currentUser.id;
props.plan = window.currentUser.plan;
}
return props;
}
</script>
<script defer
src="https://statable.com/js/YOUR_SITE_ID/s.js"
data-before-send="statableEnrich"></script>
Fires once per pageview, called with merged custom props (data-statable-* attributes + any per-call props). If the function throws, the SDK swallows the error silently and continues with unmodified props.
data-statable-* (optional)
Any attribute prefixed with data-statable- becomes a sticky custom property attached to every event sent from this page load. Useful for cohort, environment, or experiment tags.
<script defer
src="https://statable.com/js/YOUR_SITE_ID/s.js"
data-statable-cohort="beta"
data-statable-env="production"
data-statable-app-version="2024.4.28"></script>
Keys are forwarded verbatim (lowercased after the prefix), so the example produces:
These props are merged before data-before-send runs, so your hook can override or remove them.
SPA support
The SDK wraps history.pushState, history.replaceState, and listens for popstate / pageshow (back-forward cache). No configuration required. Pageviews fire automatically on client-side navigation in React Router, Vue Router, Next.js, SvelteKit, Astro, and any router that uses the History API.
If your router doesn't use history.pushState (rare), call window.statable.t('pageview') manually after each transition.
Performance impact
- Bundle: ~2.5 KB gzip (
s.js). - Loaded with
defer: zero render-blocking, runs afterDOMContentLoaded. - Network calls use
fetchwithkeepalive: trueso unload events don't delay navigation. - Engagement timer runs in a Web Worker. No main-thread cost.
- Scroll tracking uses
requestAnimationFrame. Fires at most once per frame.
See also
- JavaScript API:
window.statable.t()reference. - Event payload reference: what gets sent to
/api/event.
Ready to take control of your web analytics? Try Statable free for 30 days — no credit card required, full feature access, GDPR-compliant by default. Start your free trial or view a live demo.