// context projectglobal

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://app.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 50ms to coalesce burst navigations.

Works out of the box with:

  • React Router (useNavigate, <Link>)
  • Next.js App Router (useRouter().push, <Link>)
  • SvelteKit (goto, <a href> with data-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://app.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 pageview per route
  • Each page transition or close emits a pageview_end with duration_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.error and 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://app.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.

Next

// related

See also