Skip to content

Silo Analytics

@adpharm/silo-analytics is a drop-in replacement for @segment/analytics-next that adds automatic Time on Page tracking with built-in guardrails for SPAs (React Router) and SSR environments. You swap one import; the engagement plugin auto-registers with sensible defaults.

import { AnalyticsBrowser } from "@adpharm/silo-analytics";
export const analytics = AnalyticsBrowser.load({
writeKey: "your-write-key",
});

GA4’s engagement_time_msec is the de-facto standard for “time on page,” but its client-side mechanics have documented weaknesses: it keeps counting while a reader is idle (“AFK reading”), it leans on the unreliable beforeunload event, and it offers no first-class hook for SPA route changes. This library reproduces GA4’s verified three-condition engagement rule (the timer only advances when the page is visible and focused and within a pageshowpagehide window) and then closes those gaps:

Idle-aware

The engagement timer pauses once the reader goes idle, so “AFK reading” doesn’t inflate your numbers.

Crash-resilient

A bounded-exponential heartbeat caps data loss on a browser crash or mobile tab-kill to ≤ one schedule-step.

SPA-ready

Internal route changes are detected via Segment page events + browser back/forward (popstate); engagement resets cleanly per page-view.

Consent-aware

Optional @segment/analytics-consent-tools integration stamps every beacon with context.consent.categoryPreferences for server-side enforcement.

  • Zero-config tracking — automatically tracks a page-engagement event on every view (the eventName default is on the Configuration page).
  • Tab & visibility aware — pauses/resumes when users switch tabs or minimize.
  • Scroll depth — each beacon carries deterministic scroll_depth_max_px plus estimated percentage fields when the page is scrollable.
  • Data-quality guardrails — a minimum-time bounce threshold, a maximum-time zombie-tab cap, and idle detection.
  • Reliable deliverypagehide + visibilitychange + Safari-only beforeunload; failed beacons persist to localStorage and replay on next load.
  • Server-side dedup — every beacon carries messageId, page_view_id, and sequence so retries collapse cleanly.