Skip to content

Skip tracking in the heatmap preview iframe - #4535

Open
anandghegde wants to merge 3 commits into
umami-software:devfrom
anandghegde:fix/heatmap-preview-pageview
Open

anandghegde wants to merge 3 commits into
umami-software:devfrom
anandghegde:fix/heatmap-preview-pageview

Conversation

@anandghegde

@anandghegde anandghegde commented Sep 14, 2026

Copy link
Copy Markdown

The heatmap report shows the tracked page inside an iframe. That page has the Umami script on it, so every time someone opens a heatmap the site records a page view (and the recorder can start too, since it waits for the tracker's session).

The dashboard can't use the existing umami.disabled localStorage flag here, because the iframe is cross-origin and has its own storage. So this names the preview frame umami.disabled, and when the tracker is running inside a frame (top !== window) with window.name === 'umami.disabled', it treats that the same way it treats the localStorage flag. It's one line in each file. The frame check keeps top-level tabs that happen to carry that name (e.g. window.open(url, 'umami.disabled')) tracking normally. The recorder doesn't need a change: it only sends data once the tracker has a session, and that never happens in the preview.

Same-name frames on other sites would also skip tracking. That only affects someone who chooses to embed their own page under that name, so it seemed fine. Happy to use a different name or a different signal if you'd prefer.

Note that the preview uses whatever tracker script the site loads, so the fix only applies once the site gets the updated script.js.

How I tested it

  • Added tracker unit tests: in a frame with window.name = 'umami.disabled', no requests are sent; in a top-level window with the same name, tracking still happens (pnpm vitest run src/tracker).
  • Ran it end to end locally (Postgres in Docker, next dev). I made a test site on another origin that loads script.js and recorder.js with heatmaps turned on, clicked around in it with headless Chromium to create heatmap data, then opened Heatmaps and picked the page:
    • On dev without the change, the pageview count went from 1 to 2, and the iframe sent /api/send.
    • With the change, the count stayed at 2 across two heatmap loads, and the iframe sent nothing. The heatmap still rendered normally.
    • Visiting the test site directly with the new script still counted a page view (2 to 3).
  • biome check is clean on the changed files. pnpm check:tracker passes.

Fixes #4529


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

The heatmap report loads the tracked page in an iframe, so the site's own
tracker recorded a page view every time a heatmap was opened. Name the
preview frame umami.disabled and have the tracker treat that window name
like the existing umami.disabled localStorage flag.

Fixes umami-software#4529
@vercel

vercel Bot commented Sep 14, 2026

Copy link
Copy Markdown

@anandghegde is attempting to deploy a commit to the Umami Software Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps

greptile-apps Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR prevents heatmap previews from generating analytics by naming the preview iframe umami.disabled and teaching the tracker to treat that browsing-context name as an opt-out.

  • Adds the opt-out name to the heatmap preview iframe.
  • Applies the new condition to tracker initialization and all subsequent sends.
  • Adds a tracker test confirming that no requests are made under the new condition.
  • The opt-out check should be narrowed to framed contexts to avoid suppressing analytics in unrelated named tabs.

Confidence Score: 4/5

The PR appears safe to merge after considering a non-blocking hardening issue around accidentally disabling analytics in unrelated named top-level tabs.

The intended heatmap suppression works, but the unconditional window.name comparison broadens the opt-out beyond iframes and can silently discard analytics when a tracked page is loaded in a top-level browsing context with the same persistent name.

Files Needing Attention: src/tracker/index.ts

Important Files Changed

Filename Overview
src/app/(main)/websites/[websiteId]/(reports)/heatmaps/Heatmap.tsx Names the cross-origin heatmap preview iframe so updated tracker scripts can recognize and suppress preview traffic.
src/tracker/index.ts Adds browsing-context-name opt-out behavior, but currently applies it to top-level named tabs as well as preview frames.
src/tracker/index.test.ts Verifies that automatic and manual tracker activity sends no requests when the browsing context has the opt-out name.

Reviews (1): Last reviewed commit: "Skip tracking in the heatmap preview ifr..." | Re-trigger Greptile

Comment thread src/tracker/index.ts Outdated
disabled ||
!website ||
localStorage?.getItem('umami.disabled') ||
window.name === 'umami.disabled' ||

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Named tabs lose analytics

The raw window.name check also disables tracking in top-level tabs named umami.disabled, such as pages opened through window.open(url, 'umami.disabled') or links using that target. Because the name persists across navigation, every pageview, event, identify call, and performance event is silently dropped outside the heatmap preview. This non-blocking concern can be avoided by restricting the signal to an embedded browsing context.

Knowledge Base Used:

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch, fixed in 3678842. The window-name check now also requires top !== window, so it only applies inside a frame (the heatmap preview always is) and a top-level tab with that name keeps tracking. Added a test for the top-level case alongside the framed one.

A top-level tab opened with that name (window.open or a link target) keeps
the name across navigation, so it would silently stop tracking. The heatmap
preview is always framed, so require top !== window.
@anandghegde
anandghegde force-pushed the fix/heatmap-preview-pageview branch from 3678842 to 51d6304 Compare September 15, 2026 16:40
@mikecao

mikecao commented Sep 19, 2026

Copy link
Copy Markdown
Member

Please resolve conflicts, thanks!

@anandghegde

Copy link
Copy Markdown
Author

Conflicts resolved in 1f4ac7d — merged dev in. The only conflict was in src/tracker/index.test.ts, where your ignores clicks dispatched on non-element targets test landed next to mine; both are kept. src/tracker/index.ts merged cleanly.

npx vitest run src/tracker/index.test.ts: 4 passed. The branch still only touches Heatmap.tsx, src/tracker/index.ts and src/tracker/index.test.ts (+35 lines).

This branch has not been deployed

No deployments
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