CLItrail Docs

Add CLItrail to Framer

Where the CLItrail tag goes in Framer, in its own idiom, with the consent, CSP and client-side routing details and how to check it works.

Written for Framer (hosted sites)

#Where the tag goes

Edit Site settings → Custom Code → head (run: Once). 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" data-website="YOUR_WEBSITE_ID"></script>

Use this instead of the tag: it defines startClitrail() for your consent manager to call.

<script>
  // Loads CLItrail only after the visitor agrees. Call startClitrail() from your
  // consent manager's "accepted" callback; nothing is fetched or stored before that.
  window.startClitrail = () => {
    if (document.getElementById('clitrail')) return;
    const tag = document.createElement('script');
    tag.id = 'clitrail';
    tag.src = 'https://YOUR_SERVICE/browser.js';
    tag.dataset.website = 'YOUR_WEBSITE_ID';
    document.head.append(tag);
  };
</script>

#Client-side routing

Framer can run custom code once or on every page visit. Keep the tag on "Once". To also record your install page when visitors reach it inside the site, add this as page-level custom code on that page, set to run on every page visit:

<script>
  (() => {
    // The tag records the page the browser loaded; this records the page when it is opened in-site.
    const sdk = window.InstallAttribution;
    const loaded = new URL(performance.getEntriesByType('navigation')[0]?.name ?? location.href).pathname;
    if (sdk?.ready && location.pathname !== loaded) sdk.ready = sdk.init({ service: 'https://YOUR_SERVICE', project: 'YOUR_WEBSITE_ID' });
  })();
</script>

#Content Security Policy

Framer serves the pages; custom headers are part of its Advanced Hosting. If you set a policy there, 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. Publish the site.
  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.

#If a visit is not recorded

Start the SDK yourself: this adds the tag without data-website and calls InstallAttribution.init once it loads.

Manual start

<script>
  (() => {
    if (window.InstallAttribution) return;
    const tag = document.createElement('script');
    tag.src = 'https://YOUR_SERVICE/browser.js';
    tag.onload = () => {
      const sdk = window.InstallAttribution;
      sdk.ready = sdk.init({ service: 'https://YOUR_SERVICE', project: 'YOUR_WEBSITE_ID' });
    };
    document.head.append(tag);
  })();
</script>

Framer documentation: www.framer.com/help/articles/how-to-add-custom-code

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