Tracking
Configure the tracking script
Every script attribute, automatic events, excluding your own visits, engagement, Web Vitals and cross-domain tracking.
Snippets use YOUR_SITE_ID as a placeholder. Sign in and they're filled in with your real site id.
The default tag needs nothing but data-site. Everything on this page is optional — add attributes to the same <script> tag you already installed.
<script
defer
data-site="YOUR_SITE_ID"
data-domain="example.com"
data-cross-domain="shop.example.net,docs.example.org"
src="https://visitrack.app/tracker.js"
></script>Script attributes
| Attribute | What it does |
|---|---|
data-site | Required. Your site id (YOUR_SITE_ID). Don't change it — it's how events reach this site. |
data-domain | Root domain for the visitor cookie, e.g. example.com. Detected automatically; set it only if detection is wrong (e.g. unusual country-code domains). All subdomains of it share one visitor id. |
data-cross-domain | Comma-separated list of other domains you own, e.g. a.com,b.com. Links to them carry the visitor id so one person stays one visitor across domains. See below. |
data-cookieless | Store nothing in the browser. The visitor id becomes a server-side hash that rotates every 24 hours. Same as turning on cookieless mode in Settings. |
data-auto="false" | Turn off the automatic events listed below. Pageviews, outbound clicks, engagement and Web Vitals are still recorded. |
data-exclude="/app/*,/settings" | Paths that are never tracked, comma-separated, * as a wildcard. Checked on every event, so client-side navigation into an excluded area stops tracking too. A page can also opt out on its own with <meta name="visitrack-ignore">. |
data-allow-local | Track on localhost, 127.0.0.1, *.local and file:// pages, which are ignored by default. Useful for testing an install. |
data-debug | Log every event the script sends (and why it skipped one) to the browser console. |
Where the visitor id lives
In standard mode the anonymous visitor id is stored in a first-party cookie _vt_vid on your root domain (so www. and app. share visitors), mirrored to localStorage _ana_vid. With data-cookieless or cookieless mode, neither is written.
Calling the script before it loads
The script loads with defer, so code that runs earlier can't call window.visitrack yet. Add this one-line queue stub above the tag: calls made before the script arrives are buffered and sent once it loads.
<script>window.visitrack=window.visitrack||function(){(window.visitrack.q=window.visitrack.q||[]).push(arguments)}</script>
<script defer data-site="YOUR_SITE_ID" src="https://visitrack.app/tracker.js"></script>After that, visitrack("signup", { plan: "pro" }) and visitrack("identify", "user_123", { plan: "pro" }) work anywhere. See Custom events.
Exclude your own visits
Open any page of your site with ?vt_ignore=1 added to the URL, e.g. https://example.com/?vt_ignore=1. That browser stops sending data until you visit a page with ?vt_ignore=0. The flag is per browser and per domain, so repeat it on each device and domain you use. To exclude an office or a whole IP range instead, use Settings → Exclusions.
Automatic events
These are recorded without any code and show up next to your custom events. Turn them all off with data-auto="false".
| Event | When | Props |
|---|---|---|
file_download | A link to a file is clicked (pdf, zip, dmg, exe, csv, xlsx, docx, pptx, mp3, mp4 and similar). | url, ext |
contact_click | A mailto: or tel: link is clicked. | type (mailto or tel) |
form_submit | Any form is submitted. Field values are never read. | form (the form's id, name or action path) |
rage_click | The same element is clicked 3 times in quick succession — usually something that looks clickable but isn't. | label, element |
js_error | An uncaught JavaScript error (max 5 per page). | message, source (file name), line |
page_not_found | The page title contains "404" or "not found", or the page has <meta name="visitrack:404">. | url |
Outbound link clicks (outbound_click) are always tracked and feed the Outbound tab.
Engagement and scroll depth
For every pageview the script measures active time on the page (only while the tab is visible) and the furthest point scrolled, as a percentage of the page height. Both are sent when the visitor leaves the page or switches tabs, and power time-on-page and scroll depth in the Pages tab.
Web Vitals
Real-user Core Web Vitals are collected from your actual visitors' browsers, once per page load: LCP (largest contentful paint), INP (interaction to next paint), CLS (cumulative layout shift), FCP (first contentful paint) and TTFB (time to first byte). Browsers that don't support a metric simply don't report it.
Cross-domain tracking
Subdomains of the same root domain already share visitors through the root-domain cookie. For separate domains (say, a marketing site on example.com and checkout on example-shop.com), install the script on both with the same data-site, and list the other domain in data-cross-domain on each side:
<!-- on example.com -->
<script defer data-site="YOUR_SITE_ID" data-cross-domain="example-shop.com" src="https://visitrack.app/tracker.js"></script>
<!-- on example-shop.com -->
<script defer data-site="YOUR_SITE_ID" data-cross-domain="example.com" src="https://visitrack.app/tracker.js"></script>When a visitor clicks a link to a listed domain, the script appends their visitor id as a _vt_vid query parameter; the script on the other side picks it up, removes it from the address bar, and keeps the same id. Cross-domain linking is off in cookieless mode, since there is no stored id to carry.
What's never tracked
Localhost and file:// pages (unless data-allow-local), pages loaded inside an iframe, and automated browsers (Selenium, Playwright, Puppeteer and similar headless tools).
Something missing? Tell us.
AI agent or LLM? Read this page as markdown.