CLItrail Docs

Add CLItrail to Vanilla JavaScript (ES modules)

Where the CLItrail tag goes in Vanilla JavaScript (ES modules), in its own idiom, with the consent, CSP and client-side routing details and how to check it works.

Written for Any bundler, or native modules

#Where the tag goes

Edit index.html and src/main.js. Put the tag on every page that shows your install command; site-wide is best, after your existing analytics tags.

<script defer src="https://YOUR_SERVICE/browser.js"></script>
<script type="module" src="/src/main.js"></script>

Start it from your code

// Module scripts run after the deferred scripts before them, so the SDK is loaded here.
// Without data-website the SDK does nothing until init runs.
const sdk = window.InstallAttribution;
if (sdk) sdk.ready = sdk.init({ service: 'https://YOUR_SERVICE', project: 'YOUR_WEBSITE_ID' });

Use this in place of the start code in main.js. Loaded without data-website, browser.js reads, stores and sends nothing until init runs. Call startClitrail() once the visitor agrees; the ready check keeps it to one visit per page load.

// Call startClitrail() from your consent manager's "accepted" callback.
export function startClitrail() {
  const sdk = window.InstallAttribution;
  if (sdk && !sdk.ready) sdk.ready = sdk.init({ service: 'https://YOUR_SERVICE', project: 'YOUR_WEBSITE_ID' });
}

#Client-side routing

If your own router changes pages with history.pushState, call recordVisit() after each change (and on popstate). The page the browser loaded is recorded by main.js, not again.

Route helper

// Records a visit when client-side navigation opens a new page path. The tag records
// the page the browser loaded, so that page is never recorded twice; before the SDK has
// started (for example, before consent) it records nothing.
let recorded;

export function recordVisit() {
  if (typeof window === 'undefined') return;
  recorded ??= new URL(performance.getEntriesByType('navigation')[0]?.name ?? location.href).pathname;
  const sdk = window.InstallAttribution;
  if (!sdk?.ready || location.pathname === recorded) return;
  recorded = location.pathname;
  sdk.ready = sdk.init({ service: 'https://YOUR_SERVICE', project: 'YOUR_WEBSITE_ID' });
}
import { recordVisit } from './clitrail.js';

export function navigate(path) {
  history.pushState({}, '', path);
  render(path); // your own view update
  recordVisit();
}

window.addEventListener('popstate', () => {
  render(location.pathname);
  recordVisit();
});

#Content Security Policy

Your host's header settings (for example _headers on Cloudflare Pages or Netlify, vercel.json, or your server), or a <meta http-equiv="Content-Security-Policy"> tag: add https://YOUR_SERVICE to script-src and connect-src, and blob: to worker-src (only Safari before 26 needs that one).

script-src 'self' https://YOUR_SERVICE;
connect-src 'self' https://YOUR_SERVICE;
worker-src 'self' blob:;

#Verify it works

  1. Run your dev server or deploy.
  2. Open a page with the tag in Chrome or Firefox (accept analytics first if you gate CLItrail on consent), open the developer console and run await InstallAttribution.ready. It resolves to { ok: true, urlId, hasAnalyticsContext, expiresAt }.
  3. In the CLItrail dashboard, open Visits & identities: the visit is listed.
  4. Anything else names the cause: disabled (no consent yet, or data-enabled="false"), opfs_unavailable (the page is not served over https or from localhost), Error (the service refused the visit: add the page's origin, including a development origin such as http://localhost:3000, under Settings → Additional domains). If InstallAttribution is undefined, the tag did not load: check the Network tab and your Content-Security-Policy.

Then run your installer on the same computer and check Install events, or run the hook with --doctor.

#Notes

  • Use this when you want to pass options from code: measurementIds, providers, getContext, or adapters registered with registerAdapter before init.
  • Keeping the result in InstallAttribution.ready lets prepareInstall and the console check use it.

Vanilla JavaScript (ES modules) documentation: developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules

Using other install paths or destinations? The setup generator puts this snippet together with your hooks and destination checklist.