Scroll Tracking for Single-Page Applications (GTM)

API Connector Add-On for Google Sheets

Check out my API Connector Add-on to easily connect and pull data from thousands of platforms (e.g. Shopify, Harvest, Mailchimp, ActiveCampaign, VWO, YouTube, etc.) directly into Google Sheets.

Google Tag Manager (GTM) conveniently provides a scroll depth trigger to determine how far someone has scrolled down the page. However, this scroll depth trigger doesn’t work on websites using single page app (SPA) technology, since there is no full page load to reset the thresholds. This means that once you scroll down to the bottom and hit your maximum scroll threshold, scroll tracking on subsequent pages won’t be tracked.

This article will show how to set up scroll tracking for single page apps using GTM (and only GTM). Since GTM’s built-in scroll tracking won’t work here, we’ll modify a different scroll tracking plugin provided by Lunametrics (now known as Bounteous). They’ve done all the hard work by providing the script, we just need to make a couple small changes.

There is one pre-requisite for this to work: you’ll need some way to identify that the page has changed in your single page app. This could be through GTM’s native historychange listener or through a custom virtual pageview event pushed into the data layer each time the page changes. This kind of trigger is required for basic page view tracking, so it almost certainly already exists in your GTM container (unless you’re trying to set up scroll tracking before page view tracking, which would be rather unusual…).

While this method should work for most cases, unfortunately there will be still be sites where it does not. As always, test before publishing.


  1. Get the Lunametrics scroll tracking plugin
  2. Import the container into GTM
  3. Choose how to import the file
  4. Complete the import process
  5. Select the scroll tracking tag
  6. Edit the scroll tracking trigger
  7. Create a new tag to clear the thresholds
  8. Add a trigger to your new tag
  9. Add in your GA tracking ID
  10. Modify, preview, and publish

1. Get the Lunametrics scroll tracking plugin

Download the Lunametrics scroll tracking container file here. Right-click the link and select “Save link as” to save the file to your own computer.

2. Import the container into GTM

Log into GTM and click Admin > Import Container

3. Choose how to import the file

You’ll be prompted to choose the file to import, which should be called luna-scroll-tracking.json. Choose to save it into a new or existing workspace (I recommend choosing New, since it’s more convenient for testing, and allows you to easily scrap the whole thing if you run into any problems).

You’ll also be prompted to choose whether these new tags, triggers, and variables should overwrite or merge into your existing container. Choose “Merge” if you want to ensure that no existing tags are disrupted.

4. Complete the import process

Once you’ve chosen your settings, click Confirm to import the Lunametrics scroll tracking container into your own GTM container.

You should now see a set of new tags, triggers, and variables related to scroll tracking. This means we can now add in our modifications to make it work for a single page app setup.

5. Select the scroll tracking plugin tag

Head over to the Tags section of GTM and find the CU – Scroll Tracking – LunaMetrics Plugin. By default, they’ve configured it to fire on All Pages. However, we want it to wait for the whole page to load, because single page apps often have an issue in which the tag fires too early and immediately sends events for all scroll tracking thresholds. Click into the tag so we can modify the trigger.

6. Edit the scroll tracking trigger

Edit the trigger by clicking on the icon in the top right of the trigger section. Remove the existing ‘All Pages’ trigger.

Replace that Page View trigger with a Window Loaded trigger and click Save.

7. Create a new custom HTML tag to clear the thresholds

This step is the core modification to make the scroll tracking plugin work on our single page app. The primary issue with tracking scroll depth on single page apps is that all the pages load up front, and the scroll depth never gets reset. Therefore we are going to add in a new tag that resets this value every time the page changes in the browser.

Navigate to Tags > New > Custom HTML tag and enter the following:

  (function() {

    window.removeEventListener('resize', this._artifacts.resize);
    window.removeEventListener('scroll', this._artifacts.scroll, true);



Before you save it, add a trigger as shown in the next step.

8. Add a trigger

For your new custom HTML tag, add the trigger that you use to track page views in your single page app. As mentioned above, typically this will be a historychange trigger, or (more reliably) a custom event pushed into the data layer each time the page changes.

The whole thing should look like this:

Now, every time the page changes, this function will run to clear and reset the scroll thresholds, allowing you to track subsequent pages in your app as if your site were firing traditional page loads.

9. Add in your own GA tracking ID

Find the tag called GA – Event – Scroll Tracking and update it with your own GA tracking ID.

10. Modify, Preview, and Publish

Now that you have everything set up, enter preview mode and click around your site to verify that all is working as expected. You may also wish to adjust the thresholds for your trigger, which you can do within the original CU – Scroll Tracking – LunaMetrics Plugin tag. By default, they’ve set it to fire at 10% and 90%, as well as every 25% (i.e. at 25%, 50%, 75%, and 100%). In this example adjustment, I’ve set it to fire only at the 25% intervals, to reduce the total quantity of scroll tracking events.

You may also wish to modify your event to fire only on certain URLs. Once you’ve made your modifications and verified that all works in Preview mode, click Publish to send the new scroll tracking tags to your site.

7 thoughts on “Scroll Tracking for Single-Page Applications (GTM)”

  1. Thank you for this article! I have been working on this work a week and this solves my problem! Thank you for putting this together!

  2. Great stuff! I loved working with Luna while I was at Caterpillar. Nice mod. One interesting thing I noticed (or maybe didn’t adjust right) is that the scroll tracking doesn’t like to fire again when you return to the same page in a session. For example user moves from A (first time) to B to C to A (second time)

    It seems that the tag fires on A (first time), B, & C as expected but fails to fire when the user returns to page A for the 2nd time.

    • Thanks for letting me know! On my test site, it seems to work even on second page views, but I will update the post to note that it might not always work as expected.


Leave a Comment