Skip to content

Conversation

@maxiride
Copy link

@maxiride maxiride commented Sep 9, 2025

What's changed

Right now, a visit with a single pageview is always counted as a bounce. As discussed in #3518 and #2544, this does not work well for SPAs or landing pages. A better approach is to define a bounce as a visit with zero custom events, since custom events allow implementers to capture engagement in ways that fit their use case.

Because engagement is subjective, I introduced a configurable BOUNCE_THRESHOLD. A visit is now considered a bounce if its number of custom events is strictly less than the configured threshold. This lets each project decide what level of interaction counts as "engagement."

Concrete examples

  • BOUNCE_THRESHOLD = 1 (default)

    • events_count = 0 → 0 < 1 ⇒ bounce
    • events_count = 1 → 1 ≥ 1 ⇒ not a bounce
    • events_count = 2 → 2 ≥ 1 ⇒ not a bounce
  • BOUNCE_THRESHOLD = 2

    • events_count = 0 → 0 < 2 ⇒ bounce
    • events_count = 1 → 1 < 2 ⇒ bounce
    • events_count = 2 → 2 ≥ 2 ⇒ not a bounce
    • events_count = 3 → 3 ≥ 2 ⇒ not a bounce

Description

This pull request introduces enhancements to bounce calculations and refactors event type handling:

  • Adds a new configurable constant, BOUNCE_THRESHOLD, to define the minimum amount of custom events to count as a non-bounce. Defaults to 1.
  • Updates bounce logic in getWebsiteStats and getInsights to use events_count and to account for sessions with custom events (EVENT_TYPE.customEvent).
  • Replaces hardcoded event type values with named constants from EVENT_TYPE as seen in other project files.

The PR partially addresses #3518

Disclaimer about ClickHouse

I am not proficient with ClickHouse, edits to these queries have been made with the help of Junie, JetBrains's Agentic AI. Please review them carefully.

Replaced hardcoded `event_type` values with named constants from `EVENT_TYPE` for improved readability and to align these files to the named constant usage seen across the project.
Updated bounce calculations to account for sessions containing custom events (`EVENT_TYPE.customEvent`), improving accuracy in `getWebsiteStats` and `getInsights` queries. Standardized usage of event type constants and adjusted query logic accordingly.
…logic

Introduced `BOUNCE_THRESHOLD` constant for configurable bounce definitions. Updated bounce calculation logic in `getWebsiteStats` and `getInsights` to use `events_count`.
@vercel
Copy link

vercel bot commented Sep 9, 2025

@maxiride is attempting to deploy a commit to the umami-software Team on Vercel.

A member of the Team first needs to authorize it.

@maxiride maxiride marked this pull request as ready for review September 9, 2025 08:10
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Summary

This PR fundamentally changes how bounce rates are calculated in Umami analytics by introducing a configurable BOUNCE_THRESHOLD system. Previously, any visit with a single pageview was automatically counted as a bounce, which was problematic for SPAs and content-heavy landing pages where users can be highly engaged without triggering additional pageviews.

The new system defines bounces based on custom event engagement rather than pageview count. A visit is now considered a bounce if its number of custom events is strictly less than the configured BOUNCE_THRESHOLD (defaults to 1). This allows each project to define their own engagement criteria - for example, a blog might keep the default threshold of 1, while a SaaS application might require 2+ interaction events.

The implementation adds the BOUNCE_THRESHOLD constant to constants.ts with environment variable configuration and proper validation. The bounce calculation logic is updated in both getWebsiteStats and getInsights functions, with corresponding changes to both Prisma (relational) and ClickHouse query implementations. These queries now include subqueries or joins to count custom events per session and compare against the threshold.

Additionally, the PR standardizes event type handling throughout the codebase by replacing hardcoded numeric values (1, 2) with named constants (EVENT_TYPE.pageView, EVENT_TYPE.customEvent) across multiple query files. This improves code maintainability and readability while ensuring consistency in how different event types are referenced.

Confidence score: 2/5

  • This PR introduces complex database query changes with potential for inconsistencies between database backends that could break analytics
  • Score lowered due to identified discrepancies in ClickHouse vs relational query logic for event counting and potential performance impacts from nested subqueries
  • Pay close attention to getWebsiteSessionStats.ts and getWebsiteSession.ts where ClickHouse queries use different counting logic than relational queries

9 files reviewed, no comments

Edit Code Review Bot Settings | Greptile

@maxiride maxiride changed the title Add configurable threshold and improve bounce calculations Feat: Improve bounce calculations with configurable threshold Sep 11, 2025
@maxiride
Copy link
Author

maxiride commented Nov 9, 2025

With the recent v3 release, I suspect that some if not most of the PR needs some rewriting. I still believe a different bounce calculation logic is relevant for SPA and the linked issues in the first post. I will try to review the v3 release to update the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant