Skip to content

Optional visit duration tracking #9

@vklimontovich

Description

@vklimontovich

Summary

Track how long users spend on pages. Requires client-side JS and backends that support updateEvent.

How other tools track duration

Tool Metric Scope Idle time included?
GA4 Session duration Session Yes (includes idle)
GA4 Engagement time Per-page No (pauses on tab switch)
Plausible Engagement time Per-page No (pauses on tab switch)

Session duration = time between first and last event. Simple but misleading - if user opens tab,
walks away for 10 min, comes back, that idle time counts.

Engagement time = actual active time on page. Pauses when tab loses focus or window is blurred.
More accurate measure of user attention.

Our approach: per-page engagement time

We'll track engagement time per page (like GA4's engagement time, not session duration):

  • Start timer when page becomes visible
  • Pause on visibilitychange (tab switch) and blur (window focus lost)
  • Resume on visibility restore / focus
  • Send duration when user leaves page

This gives accurate "time spent reading this page" metric.

Design

Client-side (NextlyticsClient component)

// Pseudocode
let startTime = 0
let accumulated = 0

function onVisible() {
  startTime = Date.now()
}

function onHidden() {
  accumulated += Date.now() - startTime
}

function sendDuration() {
  const duration = accumulated + (document.hidden ? 0 : Date.now() - startTime)
  fetch('/api/event', {
    method: 'POST',
    body: JSON.stringify({
      type: 'updateEvent',
      eventId: currentPageViewId,
      patch: { properties: { duration } }
    })
  })
}

Events to listen

  1. visibilitychange - pause/resume timer
  2. pagehide - send final duration (more reliable than beforeunload)
  3. Optional: periodic heartbeat every 30s for long sessions

Page navigation (SPA)

On client-side navigation:

  1. Send duration for current page
  2. Reset timer
  3. Start tracking new page

Backend requirements

Only backends with supportsUpdates: true can use this feature:

  • ClickHouse: yes
  • Postgres/Neon: yes
  • Segment: no
  • PostHog: no
  • GA: no (but GA tracks in anyway)

Config

Nextlytics({
  trackDuration: true, // default: false
  durationHeartbeatMs: 30000, // optional, 0 to disable
})

Changes needed

  1. Add trackDuration and durationHeartbeatMs to NextlyticsConfig
  2. NextlyticsClient component:
    • Set up visibility/pagehide listeners
    • Track accumulated time
    • Send updateEvent on page leave
  3. Add type: 'updateEvent' handling to /api/event route
  4. Document which backends support this

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions