There are two ways to install invisit, and they run the same tracker built from the same source. Pick the script tag when there is no build step: static HTML, WordPress, Shopify, Framer. It works in bundled apps too. Pick the npm package when you want track(), identify() and consent() as typed imports and the tracker bundled with your app.
Both need your site id, from Settings → Installation in the dashboard. It is public and never changes, so it is safe in client code and in PUBLIC_ or NEXT_PUBLIC_ environment variables.
Script tag
<script
async
src="https://api.invisit.dev/invisit.js"
data-site="<site id>"
data-collector="https://api.invisit.dev"
></script>The tracker loads asynchronously and never blocks the page. The tag always serves the current tracker release, so updates reach your site with no change on your side. Where it goes on WordPress, Shopify and Framer is in the platform guides.
npm package
npm install @invisitdev/webOr with pnpm add, yarn add or bun add. Then call init() once, in the browser, as early as your app allows:
import { init } from '@invisitdev/web'
init({ site: '<site id>' })That is the whole setup. init() sends the first page view, follows client-side navigation from then on, and returns the running tracker. The web frameworks show where that line goes in each framework.
The package’s optional parts (web vitals, no-code rules, the feedback widget, the heatmap overlay) are dynamic imports, so your bundler splits them into your own files and the page never requests a script from a third-party host.
Calls made before init() are queued and applied once it starts, so an identify() in your auth code can safely run first.
Options
Each option is an init() field, or a data-* attribute on the script tag. Boolean attributes take true or false.
| Option | Attribute | Default | What it does |
|---|---|---|---|
site |
data-site |
required | The site id. |
baseUrl |
data-collector |
https://api.invisit.dev, none for the script |
The collector, or a proxy path such as /_i. |
requireConsent |
data-require-consent |
false |
Sends nothing until consent is granted. See Privacy. |
storage |
data-storage |
auto |
none keeps nothing at all on the device. |
excludePaths |
data-exclude-paths |
none | Paths never measured. See Excluding traffic. |
outboundLinks |
data-outbound-links |
true |
Records clicks on links to other sites. |
fileDownloads |
data-file-downloads |
true |
Records clicks on file downloads. |
respectGpc |
data-respect-gpc |
false |
Turned on, stops for browsers that send Global Privacy Control. |
respectDnt |
data-respect-dnt |
false |
Turned on, stops for browsers that send Do Not Track. |
debug |
data-debug |
false |
Logs delivery problems to the console, and lets a browser driven by automation send events, so you can test your setup. |
Everything else (allowed domains, redacted query keys, web vitals, heatmaps, no-code event rules, the feedback widget) is set per site in the dashboard and fetched by the tracker when it starts, so changing it needs no redeploy.
Group dynamic paths
Where ids or names sit in the path, such as /orders/8121 or /acme/settings, every one of them
is its own row in the Pages report. pageUrl rewrites each address before it is recorded, so one
route is one row:
init({
site: '<site id>',
pageUrl: (href) => href.replace(/\/orders\/\d+/, '/orders/:id'),
})It gets the full URL and returns a full URL; query parameters and the fragment are redacted
afterwards as usual. With the script tag, queue the same function on the invisit stub before the
script loads:
<script>
window.invisit =
window.invisit ||
function () {
;(invisit.q = invisit.q || []).push(arguments)
}
invisit('pageUrl', (href) => href.replace(/\/orders\/\d+/, '/orders/:id'))
</script>
<script
async
src="https://api.invisit.dev/invisit.js"
data-site="<site id>"
data-collector="https://api.invisit.dev"
></script>Two addresses that rewrite to the same one count as the same page, so moving between them records no new page view.
A heatmap is drawn over the page opened at its recorded address, and /orders/:id opens no page.
So clicks are not collected where pageUrl changes the path, and those pages have no click
heatmap. Rewriting only the query keeps it.
Both on one page
If a page has both the script tag and the package, whichever starts first wins and the other reuses it. A visit is never counted twice.
Single-page apps
Nothing to add. The tracker follows client-side navigation in every router and records a page view for each new address. The fragment is not part of an address, so a router that changes only #/path records one page view, not one per route.
Browser support
Chrome 91, Edge 91, Firefox 90, Safari 15 and later.