Clicktag docs
Installation
Clicktag is one async latest.js tag, about 4 KB compressed, with no npm package, for plain HTML, React with Vite or the Next.js App Router. It tracks single-page app navigation through pushState and popstate and takes its settings from data- attributes or window.tt_settings.
Script placement
Load latest.js once in your root document. Do not mount duplicate script tags across child views. For a new site, copy the tag from Site settings > Installation, step 1: keep both data-hostname and data-verify. In the examples below, replace YOUR_SITE_VERIFY_TOKEN with that site’s verify_token, not your account access token.
Plain HTML
Place the script in your shared template header or footer:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>My App</title>
<script
async
src="https://clicktag.io/latest.js"
data-hostname="example.com"
data-verify="YOUR_SITE_VERIFY_TOKEN"
></script>
</head>
<body>
<main></main>
</body>
</html>React and Vite
Add the script directly inside index.html at the project root:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<script
async
src="https://clicktag.io/latest.js"
data-hostname="example.com"
data-verify="YOUR_SITE_VERIFY_TOKEN"
></script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>Next.js (App Router)
Place a raw <script> tag inside app/layout.tsx within the <body> element. Clicktag does not require an external npm package:
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
{children}
<script
async
src="https://clicktag.io/latest.js"
data-hostname="example.com"
data-verify="YOUR_SITE_VERIFY_TOKEN"
/>
</body>
</html>
);
}The tag is included in the rendered document; tracking runs in the visitor’s browser. For strict CSP, follow your framework’s nonce or hash setup for any inline code.
For visitors without JavaScript, you can add the optional noscript pixel to the same <body>. JSX needs referrerPolicy and a style object; the HTML form of the pixel fails the type check in next build:
<noscript>
<img
loading="lazy"
src="https://clicktag.io/noscript.gif?hostname=example.com"
alt=""
referrerPolicy="no-referrer-when-downgrade"
style={{ position: "fixed", top: 0, left: 0 }}
/>
</noscript>Keep it a plain <img>, not next/image, even when @next/next/no-img-element warns.
Single-page navigation
latest.js automatically hooks the browser window.history.pushState API and listens to popstate events. These navigation events trigger pageviews when the tracked path changes. Consecutive identical paths are suppressed; replaceState alone does not trigger a pageview.
Do not add a second manual pageview for a route already covered by automatic tracking.
If your frontend uses hash-based routing (such as example.com/#/dashboard), add the data-mode="hash" attribute to track hash fragment transitions:
<script
async
src="https://clicktag.io/latest.js"
data-hostname="example.com"
data-verify="YOUR_SITE_VERIFY_TOKEN"
data-mode="hash"
></script>Manual pageview tracking
If you prefer manual control over pageviews, disable automatic collection with data-auto-collect="false". When disabled, trigger pageviews using window.tt_pageview(path, metadata).
Calls recording an identical consecutive path are automatically suppressed. Verify that the script has loaded before calling window.tt_pageview:
<script>
function trackInitialPage() {
if (typeof window.tt_pageview !== "function") {
console.warn(
"Pageview tracking is unavailable; check DNT and script loading.",
);
return;
}
window.tt_pageview(window.location.pathname);
}
</script>
<script
async
src="https://clicktag.io/latest.js"
data-hostname="example.com"
data-verify="YOUR_SITE_VERIFY_TOKEN"
data-auto-collect="false"
onload="trackInitialPage()"
></script>Script configuration options
Configure behavior by setting data attributes on the <script> tag:
data-hostname: The registered site hostname (e.g.,example.com), including when the website redirects between the apex andwww. Use the bare hostname as registered. The collector normalizes it the way registration does, so a scheme, path or stray space still matches, but a hostname with a port never does.data-verify: The site’s public ownership challenge from site settings orGET /api/sites/{hostname}. Required for automatic verification of an unverified site; existing verified sites do not need a new tag.data-ignore-pages: Comma-separated path patterns with wildcards to ignore (e.g.,/admin/*,/checkout/receipt). Each pattern must match the whole path, case-insensitively;*matches any characters.data-allow-params: Extra comma-separated query keys to keep, beyond the campaign parameters the script already recognizes (e.g.,plan). Keep values non-sensitive.data-mode: Set to"hash"for hash-based client routers.data-auto-collect: Set to"false"to disable automated pageviews on load and history navigation.data-strict-utm: Set to"true"to keep automatic campaign-parameter collection toutm_keys rather than short aliases such assource,campaignandref.data-retention: Set to"false"(orretention: falseinwindow.tt_settings) to turn off returning-visitor tracking. When on, the first pageview of each page load requests/r. The browser keeps the response fresh in its HTTP cache until the UTC day ends, so the request reaches Clicktag about once a day per browser; itsLast-Modifieddate holds the visitor's first and last visit day, for this site only. The script writes no browser storage of its own. So other tabs, hard reloads and private windows don't count twice, the server remembers that date under the day's visitor hash until the UTC day ends.data-namespace: Defaults tott;data-namespace="shop"usesshop_event,shop_metadata, and manual-modeshop_pageview, with the guardtt_shop_loaded. Configuration remains inwindow.tt_settings. Give each tracker its own namespace, and avoidsa: it gets nosa_pageview, and its default event namesa_eventis reserved.data-event-global: Overrides only the event function name (data-sa-globalis a legacy alias). Use an unused global or its preload queue, neversa_event: Clicktag reserves that name for Simple Analytics and will not consume its queue or replace its API.data-ignore-metrics: Comma-separated metrics the script should not send:referrer,utm,country,session,timeonpage,scrolled,useragent,screensize,viewportsizeorlanguage. Entries match by prefix, soscrolledalso drops screen size. Ignoringuseragentonly stops the script from sending it; the collector still reads the browser'sUser-Agentrequest header.data-collect-dnt: Set to"true"(orcollectDnt: trueinwindow.tt_settings;data-ignore-dntanddata-skip-dntare aliases) to skip the script's Do Not Track check. See Privacy and Do Not Track for what the collector still drops.data-non-unique-hostnames: Comma-separated referrer hostnames, withoutwww., whose visits should not be flagged as unique arrivals, such as your other domains or a payment provider. Visitor counts use the daily visitor hash; this flag is only their fallback for rows without one.data-path-overwriter: The name of a global function that receives{ path }and returns the path to record; a falsy return keeps the original. It runs beforedata-ignore-pagesmatching. If it throws, the script logs the error and keeps the original path.data-metadata-collector: The name of a global function called before each pageview withtype: "pageview"andpath, and before each event withtype: "event"andevent, alongside the metadata gathered so far. The object it returns is merged into that hit's metadata. If it throws, the script logs the error and sends the hit without metadata.
You can also assign settings via window.tt_settings before the script loads:
<script>
window.tt_settings = {
hostname: "example.com",
autoCollect: false,
};
</script>
<script
async
src="https://clicktag.io/latest.js"
data-hostname="example.com"
data-verify="YOUR_SITE_VERIFY_TOKEN"
></script>The default globals are tt_settings, tt_event, tt_metadata, and manual-mode tt_pageview; the default loaded guard is tt_tt_loaded. An occupied event function without a preload queue is not overwritten; the script warns while pageviews continue. For most settings, window.tt_settings overrides the equivalent attribute; data-auto-collect="false", data-retention="false" and data-strict-utm="true" still take effect. Avoid conflicting configurations and set them before latest.js executes. Clicktag does not host the optional automatic-events companion scripts.
Privacy and Do Not Track
By default, navigator.doNotTrack === "1" stops normal collection after the script loads: no pageviews or events are sent, and the script does not install tt_event or tt_pageview. Separately, the collector drops pageviews, events and engagement updates whose request carries a DNT: 1 or X-Do-Not-Track: 1 header. data-collect-dnt only skips the check in the script, which never sends the collector's collect-dnt override, so those requests are still dropped. Keep this default to respect the visitor's setting.
Content Security Policy (CSP)
Clicktag sends pageviews, events and the /r returning-visitor request as new Image() requests. /append engagement updates use navigator.sendBeacon when the visitor leaves the page; after a single-page navigation, or when sendBeacon is unavailable or refuses the data, they go as an image request to simple.gif instead. Tags served from clicktag.io or clicktag.app send beacons to clicktag.io. Existing tags served from totallytics.com or totallytics.app keep sending to totallytics.com.
If your site serves a Content Security Policy header, merge our hosts into your existing directives. Do not overwrite your wider security rules:
script-src 'self' https://clicktag.io;
img-src 'self' https://clicktag.io;
connect-src 'self' https://clicktag.io;Self-hosted copies of latest.js downloaded before 25 September 2026 still beacon to https://analytics.bitgate.dev; keep that host in img-src and connect-src as well, or download the current script.
The script-src entry must match the host in your snippet. New installations use https://clicktag.io; a tag loaded from https://clicktag.app needs that host in script-src instead. Legacy Totallytics tags still need their existing script host in script-src and https://totallytics.com in img-src and connect-src. A self-hosted copy of latest.js needs only 'self', but automatic verification and the setup check only recognize tags loaded from Clicktag hosts, so verify such a site by DNS or file. If your policy defines script-src-elem, allow the tag host there as well. Inline settings, event queues, and onload examples also need your existing inline-code policy; prefer external app code or your framework's nonce/hash mechanism rather than adding unsafe-inline.
Site settings > Installation, step 3, Run setup check fetches your homepage and checks its Content-Security-Policy response header against the detected tag host and beacon endpoints. Nonce/hash policies need a browser check. It does not read <meta> or report-only policies, and it cannot see tags injected by client-side code; with no tag in the served HTML, it checks the script host clicktag.io.
Testing on localhost
By default, latest.js extracts location.host when data-hostname is omitted. On local development environments, this results in values like localhost:3000.
The script has no localhost exclusion and also sends from local development. A local hostname such as localhost:3000 cannot be registered, so the collector ignores it; an explicit production data-hostname records local test traffic against your live site. To verify integrations, test on an explicit staging or production domain that matches a registered hostname. Do not direct localhost test traffic to your production hostname.
Next, explore Custom Events or review the Troubleshooting Guide.