GA4 Scroll Depth Tracking: The 90% Problem (And How to Fix It)
Show article contentsHide article contents
- What GA4 actually tracks when someone scrolls
- Why the 90% threshold misses most of your readers
- The GTM workaround: 10 steps across three platforms
- Five ways GTM scroll tracking silently breaks
- Tracking scroll depth without GTM
- What you lose to consent banners and ad blockers
- Why scroll depth matters more than you think
- How automatic scroll tracking works
- Frequently asked questions
GA4's scroll tracking is broken by design. It fires one event, at one threshold, once per page. No 25%. No 50%. No 75%. No per-page averages. No drop-off data. The tool that tracks your visitors' screen resolution and browser language has no idea whether anyone read past your headline.
- GA4's Enhanced Measurement fires a single scroll event when a visitor reaches 90% of the page. No 25%, 50%, or 75% breakpoints. No per-page averages. No drop-off data.
- Setting up multi-threshold scroll tracking via GTM requires 10+ steps across three platforms, a mandatory 48-hour wait for data, and breaks silently on SPAs, lazy-loaded content, and pages shorter than the viewport.
- 55% of all pageviews get fewer than 15 seconds of active attention (Chartbeat, 2 billion visits). Average scroll depth is roughly 50%. GA4's 90% threshold captures only the minority who reach the bottom.
- On a typical EU publisher site, 55-75% of scroll events are never collected due to cookie consent rejection (34-47%, USENIX 2024) and ad blockers (15-40%). GA4's behavioral modeling cannot reconstruct scroll data.
- Privacy-first analytics tools track scroll depth automatically at every percentage, with zero setup, no consent banners, and no data loss from ad blockers or consent rejection.
What GA4 actually tracks when someone scrolls
GA4's Enhanced Measurement includes an automatic "scroll" event. It fires when a visitor reaches 90% of the vertical page depth. That is the entire feature. One event. One threshold. One time per pageview.
The event sends a single parameter: percent_scrolled, always with the value 90. There is no option to adjust the threshold. There is no way to add intermediate checkpoints. GA4 does not calculate an average scroll depth, does not show a distribution curve, and does not break down scroll data per page in any standard report.
On short pages, the event is meaningless. A thank-you page, a contact form, a confirmation screen: nearly every visitor hits 90% without scrolling at all. All thresholds fire simultaneously on page load for pages shorter than the viewport. The data tells you nothing about actual engagement.
Binary: yes or no
No intermediate thresholds
No per-page scroll averages
No drop-off distribution
Fires on short pages without scrolling
Per-page averages to compare content
Where readers drop off
Scroll data combined with time on page
Data for every visitor, not just 90% scrollers
Real-time, no 48-hour delay
Julius Fedorovicius, one of the most cited GA4 experts, put it plainly on Analytics Mania: "it's very difficult to remember a case where I found scroll tracking in GA useful."
Why the 90% threshold misses most of your readers
The 90% threshold captures the tail end of reader behavior. Most visitors never get there.
Nielsen Norman Group's 2018 eyetracking study measured 130,000 eye fixations across 120 participants and found that 57% of viewing time happens above the fold. 74% of all viewing time falls within the first two screenfuls of content. People scroll more than they did in 2010, when 80% of attention stayed above the fold. But they still concentrate most of their attention at the top.
Chartbeat analyzed 2 billion page visits across the web and found that 55% of all pageviews received fewer than 15 seconds of active attention. The average reader scrolls roughly 50% of an article before leaving. Tony Haile, then CEO of Chartbeat, wrote in TIME: "We confuse what people have clicked on for what they've read."
Contentsquare's 2026 benchmarks across 99 billion sessions confirm the pattern holds: desktop scroll rates average 50.5%, mobile just 45.2%, and both are declining year over year.
On a 3,000-word blog post, 90% of the page is the footer and comments section. Setting your only data point there is like placing a store counter at the fire exit and wondering why nobody stops by.
GA4's scroll event tells you whether a minority of visitors reached nearly the bottom. It tells you nothing about the majority who left at 30%, 50%, or 70%. It cannot answer the question content creators actually need answered: where did readers lose interest?
The GTM workaround: 10 steps across three platforms
Google's answer to the 90% problem is Google Tag Manager. GTM is a separate product with its own account, container, and learning curve. Here is what the full multi-threshold scroll tracking setup looks like.
Step 1: Disable Enhanced Measurement scroll. In GA4, go to Admin > Data Streams > your stream > Enhanced Measurement > toggle off "Scrolls." If you skip this, you get duplicate events at 90% that silently pollute your data.
Step 2: Enable scroll variables in GTM. Go to Variables > Configure. Enable Scroll Depth Threshold, Scroll Depth Units, and Scroll Direction under the built-in Scrolling section.
Step 3: Create a scroll depth trigger. Go to Triggers > New. Select "Scroll Depth." Check "Vertical Scroll Depths," select "Percentages," and enter your thresholds: 25, 50, 75, 100. Set it to fire on All Pages.
Step 4: Create the GA4 event tag. Go to Tags > New. Select "Google Analytics: GA4 Event." Enter your Measurement ID. Set the event name to scroll. Add an event parameter: percent_scrolled with value {{Scroll Depth Threshold}}.
Step 5: Assign the trigger. Link the scroll depth trigger to the tag.
Step 6: Test in Preview mode. Click Preview in GTM. Enter your URL. Scroll past each threshold. Switch to Tag Assistant and verify the events fired with correct values.
Step 7: Publish the container. Click Submit. Add a version name. Publish.
Step 8: Register the custom dimension in GA4. Go to Admin > Custom Definitions > Create Custom Dimension. Set scope to Event. Enter the parameter name percent_scrolled exactly (case-sensitive). If you get this wrong, your data appears in DebugView but never shows up in reports.
Step 9: Wait 24-48 hours. Custom dimensions take up to 48 hours to populate in standard reports. There is nothing you can do to speed this up.
Step 10: Build an Exploration report. Go to Explore > Blank. Import your scroll event, the percent_scrolled dimension, and event count metric. This is the only way to see which thresholds were reached and on which pages.
That is for tracking four thresholds on all pages. Each additional page-specific trigger or threshold combination adds more configuration. And you have 50 event-scoped custom dimensions total in GA4. If you are already tracking button clicks, outbound links, and form submissions, those slots disappear fast.
Plausible Analytics calls the GTM setup "time-consuming and tricky for those unfamiliar with Google Analytics and Tag Manager" and notes that many organizations "engage freelancers and agencies to help with such setups, which is expensive and time-consuming."
Five ways GTM scroll tracking silently breaks
Even if you complete all 10 steps correctly, five common scenarios cause GTM scroll tracking to fail without any warning.
1. Single Page Applications reset nothing. In React, Vue, Angular, and Next.js apps, navigating between pages happens without a full page reload. GTM's scroll depth trigger keeps its internal state from the previous page. If a user scrolled to 100% on Page A and navigates to Page B, the trigger considers all thresholds already crossed. It fires nothing on Page B. Or any subsequent page in the session. A Gatsby GitHub issue documented this exact behavior: scroll events fired on the first page, then stopped completely on all client-side route changes. A react-gtm issue asking how to reset scroll depth on navigation has been open since 2023 with zero comments.
Simo Ahava, the most recognized GTM expert in the industry, explicitly lists the ability to "reset the trigger manually, which is useful especially on single-page apps" as a desired but missing feature in GTM.
2. Lazy loading changes the page height after calculation. GTM calculates scroll percentages based on the initial document height. When images, iframes, or content load below the fold, the page grows. A user who reached what GTM calculated as "75%" may have actually seen only 50% of the final content. Google's own autotrack library documentation states it "works best on pages whose height doesn't change once the page has loaded" and is "not recommended" for infinite scroll.
3. CSS overflow containers are invisible to GTM. Modern web layouts frequently use overflow-y: auto on a container div with overflow: hidden on the body. Dashboard apps, chat interfaces, and full-screen SPA layouts all use this pattern. GTM's scroll trigger only monitors window-level scroll events. Scrolling inside a container div never registers. The page appears to have zero scroll engagement.
4. Short pages fire all thresholds instantly. Simo Ahava documented that if a page is shorter than or equal to the viewport height, all scroll depth thresholds fire immediately on page load without any user scrolling. Contact pages, thank-you pages, and short landing pages all trigger 25%, 50%, 75%, and 100% events the moment they load. Your data shows perfect engagement on pages where nobody scrolled at all.
5. Duplicate events from not disabling Enhanced Measurement. The most common implementation error. If the GA4 Enhanced Measurement "Scrolls" toggle remains on while custom GTM scroll tracking is also running, every 90% scroll fires twice under the same scroll event name. The data merges in reports. There is no way to separate them retroactively.
81% of GA4 implementations contain errors that compromise data accuracy. Scroll tracking is especially fragile because it depends on consistent page structure, a single scrolling context, and correct parameter naming across two separate Google products.
Tracking scroll depth without GTM
If you do not want to use Google Tag Manager, you can track custom scroll thresholds using gtag.js directly. This approach requires JavaScript on your site.
(function() {
var thresholds = [25, 50, 75, 100];
var fired = {};
var ticking = false;
function getScrollPercent() {
var docEl = document.documentElement;
var body = document.body;
var scrollTop = docEl.scrollTop || body.scrollTop;
var scrollHeight = Math.max(
docEl.scrollHeight, body.scrollHeight,
docEl.offsetHeight, body.offsetHeight
);
var clientHeight = docEl.clientHeight;
if (scrollHeight <= clientHeight) return 100;
return (scrollTop / (scrollHeight - clientHeight)) * 100;
}
function check() {
var pct = getScrollPercent();
for (var i = 0; i < thresholds.length; i++) {
var t = thresholds[i];
if (pct >= t && !fired[t]) {
fired[t] = true;
gtag('event', 'scroll', {
percent_scrolled: t,
page_path: window.location.pathname
});
}
}
ticking = false;
}
window.addEventListener('scroll', function() {
if (!ticking) { ticking = true; requestAnimationFrame(check); }
}, { passive: true });
})();
This fires GA4 events at 25%, 50%, 75%, and 100% with passive scroll listeners and requestAnimationFrame throttling for performance. The { passive: true } flag tells the browser not to wait for the handler before scrolling, avoiding scroll jank.
The approach works, but has the same fundamental limitations as GTM:
- You still need to register
percent_scrolledas a custom dimension in GA4 - You still wait 24-48 hours for data to appear in reports
- SPAs require additional router integration to reset the
firedthresholds on navigation - Lazy-loaded content changes the page height after the initial calculation
- Parameter values are silently truncated at 100 characters
- The script requires the GA4 gtag.js library to be loaded first
For an IntersectionObserver approach, you would place invisible sentinel elements at each threshold point and observe them entering the viewport. This runs off the main thread and avoids scroll listener performance issues, but requires DOM access to insert the sentinel elements.
Neither method solves the consent and ad blocker problem described in the next section.
What you lose to consent banners and ad blockers
Suppose you completed the GTM setup perfectly, or added the gtag.js code above, and your scroll tracking is live. You are still missing more than half of your actual scroll data.
Cookie consent rejection loses 34-47% of EU visitors. When EU visitors reject the cookie consent banner, GA4 either stops tracking entirely (Basic Consent Mode) or sends anonymous pings without session context (Advanced Consent Mode). A 2024 USENIX Security study across 3,947 participants found rejection rates of 34% with equal-prominence banners and up to 47% with transparency-focused designs. The etracker 2025 study found that legally compliant German banners average 40-54% consent rates, meaning 46-60% of visitors opt out.
Ad blockers hide another 15-40% of visitors. Extensions like uBlock Origin, Ghostery, and Brave's built-in shields block requests to google-analytics.com and googletagmanager.com. These visitors scroll your pages, but GA4 never loads. In Europe, ad blocker usage reaches roughly 40%. For tech-focused audiences, Plausible measured 58% GA blocking during a tech-audience traffic spike.
GA4's behavioral modeling cannot reconstruct scroll data. GA4 models certain metrics for unconsented users: active users, sessions, conversions. But event counts, including scroll events, are not modeled in standard reports. Your scroll data has a permanent hole that no algorithm fills.
Cookieless analytics tools avoid the consent problem entirely. When a tool stores nothing on the visitor's device and does no fingerprinting, it falls outside the scope of ePrivacy Article 5(3), which means no consent banner is required. No consent banner means no data loss from rejection. And because cookieless trackers do not call google-analytics.com or googletagmanager.com, they are not caught by the filter lists that block GA4.
Why scroll depth matters more than you think
Scroll depth is not a vanity metric. It directly affects content quality, ad revenue, CTA placement, and, indirectly, search rankings.
Content optimization with real data. Neue Zurcher Zeitung, Switzerland's oldest newspaper, used Chartbeat's scroll depth feature to identify where readers dropped off. They tested subheadings versus plain text versus bullet points. Subheadings won. Average engaged time increased nearly 20%, from 51 seconds to over 60 seconds. Their head of audience engagement said the feature "helps us understand when exactly our readers are dropping off, find patterns, and get creative with layouts, copy, images, and more."
The Financial Times built an internal tool called "Lantern" that tracks scroll rate, retention rate, and average time on page per article. They found that "quality reads" (sessions where a reader consumed at least half the article, estimated by scroll depth and time) are a stronger predictor of subscription conversions than raw pageviews.
Ad revenue is directly tied to scroll depth. Display ad viewability requires 50% of pixels visible for at least one second (IAB standard). One publisher raised viewability from 35% to roughly 60% using lazy loading and saw an 80% eCPM jump on those ad units. Scroll depth tracking reveals where users stop, letting you position ads at ideal points before abandonment. Chartbeat found that 66% of engaged time happens below the fold, contradicting traditional ad placement wisdom.
Engagement signals influence search rankings. Google's Navboost system, described as "one of the important signals" by Google VP Pandu Nayak during the DOJ antitrust trial, uses click quality signals (good clicks, bad clicks, last longest clicks) to assess content satisfaction. A 2024 leak of Google's internal API documentation confirmed the system's scope. Scroll depth is not confirmed as a direct signal, but the behaviors it reflects, whether visitors stay and engage or bounce immediately, feed directly into ranking. As Andy Crestodina noted on the CoSchedule podcast, the most visible metrics are often the least connected to business success.
Without scroll data, you are guessing. With it, you know which pages work, where readers lose interest, and where to place CTAs and ads. The bounce rate alone cannot tell you this.
How automatic scroll tracking works
Clickport takes a different approach. The tracker captures the maximum scroll percentage (0-100%) on every page, automatically, with zero configuration. No GTM. No custom dimensions. No thresholds to define. No 48-hour wait.
The tracker observes scrollTop relative to the document height using a ResizeObserver to handle dynamic content. On page leave, it sends the maximum scroll depth reached along with engaged time. The data appears in your dashboard in seconds.
The dashboard shows scroll depth as a KPI percentage averaged across all sessions. You can see per-page scroll averages in the content panel, scroll trends in the hourly chart, and scroll depth is factored into the engagement score that combines scroll and time metrics into a single quality number.
Scroll depth is also built into the bounce calculation. A single-page session with less than 25% scroll depth, no clicks, and under 15 seconds of active time counts as a bounce. A visitor who scrolls past 25% is not a bounce, even on a single-page session. This is a more accurate reflection of engagement than GA4's arbitrary 10-second engagement time threshold.
| Feature | GA4 (default) | GA4 + GTM | Clickport |
|---|---|---|---|
| Thresholds tracked | 90% only | You define (25/50/75/100) | 0-100% continuous |
| Setup required | None | 10+ steps, 3 platforms | None |
| Per-page averages | No | Via Explorations only | Built-in |
| Data delay | 24-48 hours | 24-48 hours | Seconds |
| Works on SPAs | Partial | Breaks silently | Yes |
| Consent banner needed | Yes (cookies) | Yes (cookies) | No |
Because the tracker uses no cookies and no personal data processing, it does not trigger ePrivacy consent requirements. No consent banner means no data loss from rejection. And at 1.9 KB gzipped, the tracker adds negligible page weight compared to GA4's 134 KB gtag.js.
Start your free 30-day trial. No credit card. One script tag. Scroll data on every page in 30 seconds.
Frequently asked questions
Does GA4 track scroll depth automatically?
Partially. GA4's Enhanced Measurement fires a single "scroll" event when a visitor reaches 90% of the vertical page depth. It does not track 25%, 50%, or 75%. It does not show per-page scroll averages. It does not show a distribution of how far visitors scrolled. For multi-threshold tracking, you need Google Tag Manager or custom JavaScript.
Why does GA4 only track 90% scroll depth?
Google has not explained the choice. The 90% threshold likely represents "reached the bottom of the page" since footers, comments, and related content sections typically occupy the final 10%. However, research from Chartbeat and Nielsen Norman Group shows that most readers scroll to roughly 50% of a page, making 90% a metric that captures only the minority who nearly finish.
How do I track 25%, 50%, 75% scroll depth in GA4?
Two options: (1) Google Tag Manager with a Scroll Depth trigger configured for vertical percentages at your desired thresholds, linked to a GA4 Event tag. (2) Custom JavaScript using gtag.js with scroll event listeners that fire gtag('event', 'scroll', { percent_scrolled: threshold }) at each checkpoint. Both require disabling the default Enhanced Measurement scroll event and registering a custom dimension. Data takes 24-48 hours to appear.
What is a good scroll depth percentage?
It varies by content type. Blog posts and long-form articles: 55-70% average scroll depth is healthy. Landing pages: 30-50% is typical since CTAs are placed high. Product pages: 40-60%. Educational content: 70-90%. Below 30% average on content pages suggests the content or design needs attention. Above 75% indicates strong engagement. The Contentsquare 2026 benchmark across 90 billion sessions shows desktop scroll rates averaging 50.5% and mobile 45.2%.
Does scroll depth affect SEO?
Not directly. Google does not use GA4 scroll data for rankings. However, the engagement behaviors that scroll depth reflects, whether visitors stay and read or bounce immediately, do affect rankings. Google's Navboost system, confirmed in the DOJ antitrust trial, uses click quality signals to rank results. Pages where visitors engage deeply tend to rank better than pages where visitors quickly return to search results.
Why is my GA4 scroll tracking not working?
The most common causes: (1) Enhanced Measurement "Scrolls" toggle is off and no custom tracking is set up. (2) Custom dimensions were not registered in GA4 Admin, so parameters are collected but invisible in reports. (3) Ad blockers or consent mode are blocking the gtag.js script entirely. (4) On SPAs, the scroll depth trigger does not reset between client-side route changes. (5) CSS overflow: hidden on the body with scrolling inside a container div makes window-level scroll events invisible to GTM.
Can you track scroll depth without Google Tag Manager?
Yes. You can add custom JavaScript that listens for scroll events and calls gtag('event', 'scroll', { percent_scrolled: value }) at each threshold. This requires the GA4 gtag.js library to be loaded on the page. You still need to register custom dimensions and wait 24-48 hours for data. Alternatively, privacy-first analytics tools like Clickport track scroll depth automatically with zero code and zero configuration.

Comments
Loading comments...
Leave a comment