SPAs & client-side routing
Single-page apps work out of the box: the snippet patches history.pushState/replaceState and listens for popstate, so every client-side navigation fires a page_view with no extra configuration. Only hash-based routers need to opt in.
History-based routers
Section titled “History-based routers”React Router, TanStack Router, Vue Router (history mode), Next.js, and anything else that uses the browser’s real history API work out of the box. Every navigation fires a page_view for the new pathname + search. Consecutive calls to the same path are deduped, so a replaceState that doesn’t actually change the path won’t double-count.
<script src="https://cdn.analytics.canarycoders.es/lytics.js" data-domain="myapp.com" defer></script>Hash-router SPAs
Section titled “Hash-router SPAs”Add the data-hash attribute to opt in:
<script src="https://cdn.analytics.canarycoders.es/lytics.js" data-domain="myapp.com" data-hash defer></script>With data-hash present, two things change:
- The tracked
pathbecomespathname + search + hash, so/#/aboutis reported distinctly from/#/pricing. - A
hashchangelistener fires apage_viewon every hash navigation, in addition to thepopstate/patched-historylisteners.
Without data-hash, hash changes are invisible to CanaryLytics, and the app looks like a single-page site with no route breakdown.
Link clicks
Section titled “Link clicks”Plain left-clicks on same-origin <a> elements are tracked as link_click events (target path only). Modified clicks (Cmd/Ctrl/Shift/Alt-click, opening a new tab), #/mailto:/tel:/javascript: links, and external links are all deliberately left untracked.