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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions