Skip to content

Conversation

fauzora
Copy link

@fauzora fauzora commented Sep 4, 2025

Issue #3596

Problem

When the Umami tracker script is injected more than once (e.g. by frameworks that
re-render , such as TanStack Start), the tracker is initialized multiple times,
causing duplicate pageview/event tracking

Solution

Add a global guard (window.__umami_tracker_loaded) at the top of the tracker script

Copy link

vercel bot commented Sep 4, 2025

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

A member of the Team first needs to authorize it.

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 fixes a critical issue where the Umami tracker script could be initialized multiple times when injected repeatedly by modern JavaScript frameworks. The problem occurs when frameworks like TanStack Start re-render the <head> section, causing the same script to execute multiple times and resulting in duplicate pageview and event tracking.

The solution implements a simple but effective global guard mechanism by adding two lines at the very beginning of the tracker's IIFE (Immediately Invoked Function Expression). The fix checks for the existence of window.__umami_tracker_loaded and immediately returns if it's already set, preventing re-initialization. If the flag doesn't exist, it sets it to true and continues with normal execution.

This change integrates seamlessly with the existing tracker architecture without modifying any core functionality. The tracker script (src/tracker/index.js) is designed to be a standalone module that handles all analytics tracking for websites using Umami. By adding the guard at the top level of the IIFE, the fix ensures that all subsequent initialization code (event handlers, data collection, API calls) only runs once, regardless of how many times the script tag is injected into the DOM.

The approach follows established patterns in JavaScript for preventing script re-execution and maintains backward compatibility since it only adds a simple global flag that doesn't interfere with existing functionality.

Confidence score: 5/5

  • This PR is extremely safe to merge with virtually no risk of causing issues
  • Score reflects a minimal, well-targeted fix that addresses a specific problem without touching core functionality
  • No files require special attention as the change is isolated and straightforward

1 file reviewed, no comments

Edit Code Review Bot Settings | Greptile

@mikecao
Copy link
Collaborator

mikecao commented Sep 4, 2025

Couldn't you just check for window.umami?

@fauzora
Copy link
Author

fauzora commented Sep 5, 2025

Couldn't you just check for window.umami?

checking window.umami is indeed more straightforward actually.
but I used __umami_tracker_loaded on beginning of script to follow the same pattern as GA4, where they rely on window.dataLayer = window.dataLayer || [] instead of checking window.gtag.

if two async scripts are loaded at the same time, there is a possibility where both script could pass window.umami before its assigned, leading to duplicate initialization. With my solution, i hope we can avoid that edge case and make the guard more robust, even though its rare in real practice.

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.

2 participants