Accurate Bounce & Time-on-Site Metrics for Long Single-Page Visits (Heartbeat & Engagement Events Support)
Describe the feature or enhancement
Hi Umami team,
The problem
For many content-driven websites (and especially on single-page apps or landing pages), most user engagement happens on a single page, often the homepage. If a visitor spends several minutes reading, scrolling, and interacting—but does not navigate away or reload—the current Umami analytics engine:
- Counts the visit as a bounce
- Records time on page as zero seconds
This is because both bounce and time-on-site metrics are calculated using only pageview events, and do not factor in custom events or signals of engagement.
This results in unrealistically high bounce rates and zero time-on-site for highly engaged visitors, which is misleading for site owners and makes it hard to assess actual user engagement.
Proposed Solution
1. Add a native “heartbeat” mechanism to the JS tracker
- The Umami tracker could automatically send periodic engagement events (e.g., every 15/30 seconds), but only while there is evidence of user activity—such as scroll, mouse movement, keyboard input, or visibility changes.
- Heartbeats should stop when the tab is hidden, page is unloaded, or there is no further user activity for N seconds.
2. Upgrade backend logic to count these “engagement” events
- When calculating bounce rate and time-on-site, sessions with at least one “engagement” (heartbeat and others) event in addition to a pageview should not be counted as bounces.
- Time-on-site for single-page sessions should be calculated as the interval between the first pageview and the last engagement event (mirroring the way Google Analytics and Matomo allow custom events to break bounces).
3. Add a configuration option
- Site owners should be able to enable/disable this feature for their sites, so those who want to retain the old behavior (pageviews only) can do so.
4. Document recommended practice
- The tracker documentation should clearly recommend when and how to use this option (e.g., for blogs, documentation sites, SPAs, etc.), and how it affects metrics.
Benefits
- Much more accurate bounce rate and time-on-site stats for content-rich or SPA sites
- No need for JS hacks or artificial “pageview” duplication workarounds
- Cleaner, more reliable engagement analytics—competitive with GA, Plausible, Matomo, etc.
We’ve also run into the same situation. Most of our projects are SPAs, and after integrating Umami we noticed extremely high bounce rates.
I still need to investigate further, but I’m wondering:
- would implementing custom events help Umami treat a session as non-bounce?
- or maybe by adding a snippet to the SPA to manually fire tracker functions? If so what should be called to register a non-bounce as a workaround for the issue?
Thanks for bringing up this issue. We do plan to add more accurate time tracking similar to what you described.
@mikecao what’s the team’s position on adjusting the bounce calculation formula?
Currently, wouldn’t it make more sense to define a bounce as a visit with only a single event (pageview)? On the backend, queries could be refactored to count bounces as visits without any events beyond the initial pageview, which shouldn’t require large code changes if I understood the implementation correctly.
I’d be interested in drafting a PR, but before putting in the effort I’d like to confirm if the team is open to this change.
Ref: https://github.com/umami-software/umami/discussions/2544#discussioncomment-14293538
@maxiride Yes, we should count further events as a non-bounce.
any update(especially for heartbeat design like https://developer.matomo.org/guides/tracking-javascript-guide#accurately-measure-the-time-spent-on-each-page )? My visit duration and bounce rate are always 0 and 100%... Also I can help with the PR
any update(especially for heartbeat design like https://developer.matomo.org/guides/tracking-javascript-guide#accurately-measure-the-time-spent-on-each-page )? My visit duration and bounce rate are always 0 and 100%... Also I can help with the PR
I did make a draft PR https://github.com/umami-software/umami/pull/3614 (before the v3 major update, so it needs a complete review since the v3 release) to introduce a better bounce calculation. I didn't address the heartbeat yet.