Pageviews & SPA Tracking
When data-analytics="pageviews" (or all) is set, the SDK fires a pageview event on every page load. For single-page apps, add spa to capture client-side route changes too.
Initial pageview
Fires once on every fresh page load, after DOMContentLoaded (so document.title is final). Payload:
{
"event_type": "pageview",
"url": "https://example.com/pricing",
"path": "/pricing",
"referrer": "https://news.ycombinator.com/",
"visitor_id": "01HZ7XK4N8...",
"session_id": "01HZ7XK4P2...",
"timestamp": "2026-05-14T10:00:00.000Z",
"custom_props": {
"viewport": "1920x1080",
"title": "Pricing | Acme"
}
} If Loguro.identify() has been called, user_id is also attached.
SPA route changes
Enable with:
<script
src="https://logu.ro/loguro.js"
data-api-key="brk_..."
data-analytics="pageviews,spa"
></script> (Or data-analytics="all".)
The SDK monkey-patches history.pushState and history.replaceState, and listens to popstate (browser back/forward). On any of those, a new pageview is queued — debounced 150ms to coalesce burst navigations.
Same-path dedup
If pushState or replaceState fires for the same path the user is already on, the SDK ignores it for 30 seconds. Frameworks call replaceState constantly during hydration, scroll restoration, and search-param toggles — counting each as a pageview would inflate path counts 5–10×.
Real reloads after 30s+ still count. A genuine route change always counts. Only ?query=foo flips and other same-path noise are dropped.
Works out of the box with:
- React Router (
useNavigate,<Link>) - Next.js App Router (
useRouter().push,<Link>) - SvelteKit (
goto,<a href>withdata-sveltekit-preload-data) - Vue Router (
router.push) - Any framework that uses the History API
You don’t need to wire anything up framework-side. The patch on history is universal.
What if my framework uses hashes? (#/about)
popstate fires on hash changes too, so the SDK captures them. path will include the hash (e.g. /#/about).
Manual pageview
For frameworks that don’t use the History API, or for splash-screen / modal flows that should count as a “view”:
window.Loguro.pageview(); // current URL + title
window.Loguro.pageview({ url: '/checkout/step-2' }); // override path
window.Loguro.pageview({ url: '/cart', title: 'Your Cart' }); Time on page
Enable with:
data-analytics-time-on-page="true" Before the next pageview (SPA navigation) or on pagehide, the SDK emits a pageview_end event with the milliseconds since the last pageview started:
{
"event_type": "pageview_end",
"path": "/pricing",
"url": "https://example.com/pricing",
"visitor_id": "01HZ...",
"session_id": "01HZ...",
"timestamp": "2026-05-14T10:01:23.000Z",
"custom_props": { "duration_ms": 83400 }
} Use to compute median time on page, bounce rate, etc.
Scroll depth
Enable with:
data-analytics-scroll="true" The SDK passively listens to scroll events (passive listener — zero perf cost) and tracks the maximum scroll percentage reached. Reported with the pageview_end event:
{
"event_type": "pageview_end",
"custom_props": {
"duration_ms": 83400,
"max_scroll_pct": 67
}
} Useful for content engagement: do readers actually reach the bottom of long-form articles?
Combined recommended setup for a marketing site
<script
src="https://logu.ro/loguro.js"
data-api-key="brk_..."
data-analytics="all"
data-analytics-time-on-page="true"
data-analytics-scroll="true"
></script> With this:
- Every page load fires a
pageview - SPA navigations fire a new
pageviewper route - Each page transition or close emits a
pageview_endwithduration_ms+max_scroll_pct - CTAs marked
[data-loguro-event]fire automatically - Web Vitals (LCP, CLS, INP) report once per page
- Long tasks > 50ms are logged as warnings
console.errorand unhandled exceptions still flow into your Loguro logs
Filtering / excluding pages
The SDK doesn’t have a built-in URL exclusion list. If you want to skip tracking on certain routes (admin pages, internal dashboards), wrap your scripts:
<script>
// Only load Loguro on public pages
if (!window.location.pathname.startsWith('/admin')) {
var s = document.createElement('script');
s.src = 'https://logu.ro/loguro.js';
s.setAttribute('data-api-key', 'brk_...');
s.setAttribute('data-analytics', 'all');
document.head.appendChild(s);
}
</script> Alternative: Loguro.setEnabled(false) on routes you don’t want tracked, then re-enable elsewhere.