How to Fire a Virtual Pageview in Google Tag Manager



Firing a Google Analytics Virtual Pageview with Google Tag Manager is easy, and far more powerful than ever before.

When our clients or training attendees upgrade from classic Google Analytics to Universal Analytics implemented through Google Tag Manager, we often get questions about how to transition these Virtual Pageviews from inline code to being implemented through GTM.

There are a number of different ways you can implement Virtual Pageviews, and hopefully this will provide one solution to help ease the transition to a GTM implementation of Google Analytics.

What Is a Virtual Pageview

Virtual Pageviews, for those not in the know, are when we send page hits to Google Analytics, without reloading the page. A standard page hit occurs when Google Analytics loads on a page. Sometimes though we want to fire ANOTHER page hit without actually going to another page, or reloading the page itself.

There are various reasons you would want to do this:

  • A contact form that submits dynamically to itself without reloading the page
  • Tracking downloads of PDF files on your site as pages
  • Modal popup windows of images, or with information capture forms
  • Multi-step dynamic shopping cart pages

For these concepts and more, Virtual Pageviews are the answer to your problem. You can fire a Pageview just as if the page was loaded, and Google Analytics will treat it as just another page hit on the site, allowing the use of URL Destination goals, Goal Funnels, and more.

How Was It Fired Before

Prior to Google Tag Manager a Virtual Pageview was fired in primarily one of two ways. Either within a script, or inline. When it was within JavaScript you could include a line like below inside any other code, and as long as Google Analytics was loaded on the page, it would fire a Pageview hit.

Classic Google Analytics Example:

Universal Analytics Example:

This code could also be placed “inline” on elements on a page, such as within the HTML code for a link, so that when someone clicked a link, certain JavaScript within the link itself would fire. This has been fairly common, but it’s prone to human error, with sometimes hundreds of links manually tagged.

Implementing or changing any of these Virtual Pageviews could be difficult, and require developer resources and time that simply didn’t exist.

How Can We Fire a Virtual Pageview With Google Tag Manager

First obviously, you need to install Google Tag Manager on your website.

If you are able to access the code of the website, it can be relatively easy to swap code. Instead of the above code snippets, we are going to do a dataLayer.push that looks like this:

The dataLayer is an object that Google Tag Manager reads on the page, and it lets us send instructions to Google Tag Manager. In this case we’re saying “An event named ‘VirtualPageview’ is occuring. The value of virtualPageURL is ‘/order/step1/ and the value of ‘virtualPageTitle’ is ‘Order Step 1 – Contact Information“.

Obviously this would change depending on what fake virtual URL or title you wanted to have sent to Google Analytics. This code can be put inline, or can be put into a JavaScript function anywhere on the page, and when it runs, it will insert those instructions into the dataLayer which will be read and acted upon by Google Tag Manager.

Creating the Necessary Tags, Rules, and Macros

We could implement that code in 100 places though, and nothing would fire, because we still need to set it up in Google Tag Manager. That part is simple.

First create two macros, one for virtualPageURL and one for virtualPageTitle both as Data Layer Variable types.




Next create a rule for {{event}} containing VirtualPageview


Lastly create a Universal Analytics Pageview tag, that fires on the rule you created, and in its configuration under More Settings, and Basic Configuration, put the macro values you just created in place for Document Path, and Document Title.


Save and Preview/Debug!

Now when that code on the page fires, it will fire a Vitual Pageview with whatever values that dataLayer.push passes. Those hundreds of inline and script implementations will all fire off those 2 macros, 1 rule, and 1 tag.

But what if we can’t access the code, or don’t want to

Sometimes you actually can’t access the code and put those dataLayer.push lines in, or maybe you don’t even want to. How about our example of having thousands of pdf files that are on your site, and you want to track them all as a Virtual Pageview from within Google Tag Manager without modifying any of their links? No problem.

First, if you don’t already have one, create a Link Click Listener tag in Google Tag Manager and either put it on All Pages, or on the pages you want to track your PDFs.


Second, create a rule that fires when event contains gtm.linkClick (i.e. someone clicked on a link) and where {{element url}} matches the regular expression (regardless of case) for the PDF extension as shown in this screenshot. {{element url}} is a prebuilt macro that should already exist in your GTM and in this case, just pulls in the URL of the link that was clicked.


Third, create a tag that fires a Pageview on that rule, and replace the Document Path and Title in the Basic Configuration settings for that Pageview with the element url and title so that the URL of the Pageview is the PDF file location, and the title of the page view is the text in the link for the PDF.


As long as your developers have put Google Tag Manager on your site, you could have a million PDF downloads, and by adding a single rule and two tags you are now tracking all of them as Virtual Pageviews really in a matter of minutes.

What else? What if it’s a modal window and I’m not looking for a file extension?

You can look for some aspect of the link that causes the Virtual Pageview to happen. For instance, maybe you have a dynamic contact form, and it has a form that submits. Find the name of the form by looking at the source code of the page, for something like this:

<form id=”testForm”>

The form ID is a unique value that you can use to target that specific form. If you want to fire a Virtual Pageview when someone submits that form just do the following:

First, create a Form Submit Listener tag and put it on the page you want with a firing rule (I’m using All Pages).


Next make a new rule with the conditions of an event containing gtm.formSubmit (i.e. a form was submitted), as well as where {{element id}} contains (or equals) that ID value, which in this case is testForm.


Finally, make a new Pageview tag, and have it fire on the Form Submit rule you just made, where you specify the values of the Document Path and Title again.


Notice here how I put all of these Virtual Pageviews into their “directory.” This helps with clearly identifying Virtual Pageviews from regular Pageviews, and also makes it easier to filter these out of a view if needed.

It’s that easy. You can use the same techniques to essentially track the clicks of any element, link, or submission of any form on a page as a Virtual Pageview. Is there a button or link that sends the user to the next virtual step in your contact form? Do it on that. Does that link launch a lightbox window? Track it.


Tracking Virtual Pageviews with Google Tag Manager is a huge improvement on the old way of doing things. It’s easy, and with Google Tag Manager it’s fast. Your transition to a GTM and UA implemenation might actually be sped up using some of these tricks, and hopefully you’ll be encouraged to track more things on your page where before you may have hesitated because of the development cost.

And don’t forget…

Remember. You defense. Points come. Concentrate. Focus power. Remember balance. Make good fight.


You’re the best! AROUND! Nothing’s gonna ever keep you down!

Sayf is a former LunaMetrician and contributor to our blog.

  • Now this is a great article few good ones about VPN online, one thing could you please tell us how would you add VPV for something other than pdfs but lets say you have a list of 5 form elements how would you just as easily automate all 5 of those inputs to be track and the title be the ID for the form field. That would be a fantastic follow up article or comment 🙂 thank you.

  • example your “leave a reply” form. 3 inputs and a submit. Also you probably like the vpv to be trigger onblur? I just haven’t found any example that explain this. 🙂 thx

  • Very helpful tutorial, thank you. There is so much to explore with GTM, lots of ideas we can implement easily and in a more effective way. We do need these kind of posts!

  • Sayf Sharif


    There is a Form Submit listener in Google Tag Manager. If you want to fire a Virtual PageView when the form gets submitted, then you can just look for the form submission that way based on the form id, and fire the virtual pageview based on that form being submitted.

    As far as the inputs in the form, maybe I’m misunderstanding you, but I would not use virtual pageviews to show interactions with the specific input elements on a form. If I wanted to track the form, I would use id’s on the input fields, listen with a general Click Listener, and then fire events based on the click of the element.

  • I am refering to your comment from 09/11. Can you explain a bit more how the tracking of forms works with GTM? You mentioned using IDs on the input fields. Can you give an example of this? I am new to GTM. Thanks for this great tutorial.

  • Sayf Sharif


    GTM has several “listeners” like the Link Click Listener, and one of them is a Form Listener, that will fire an event in GTM when a form is submitted.

    So with a Form Listener on a page through GTM, we can fire an event tied to when that specific form has been submitted.

    Here’s a link to a great blog post by Simo Ahava on Advanced Form Tracking:

  • Amazing blog post. 15 Days ago i was working 30 minutes with a co-worker trying to do this, and we failed miserably.

    Now, I found this blog post, and we managed to set everything up in ten minutes.

    Thanks a lot!

  • Max

    Thanks for the great writeup – any thoughts on using this to fire a virtual page view on a history state change, such as on a site that uses the new HTML5 pushState API?

  • Uri Klar

    Thanks for the great post! It was simple and to the point

  • Lee

    I have wordpress site and I’m using contact form 7 plugin on my two forms. I’m trying to used the gtm.formSubmit method. How can I track each form submitted using page view in google analytics goal conversion? Here is my screenshot of the goals

  • Sayf Sharif


    What you’ll need to do is look at each form and use each ID independently.

    The screenshot for it shows in the top area the code to copy onto the page, within which is ID=”1056″ in the sample. Yours will be a different number, however that number is how you identify it. Use the instructions above but make the element id for the form the number listed here, not id=12345, but just the 12345 number itself. That then will be a rule that says “when someone submits form 12345”. That way you could have multiple forms, each with different numbers, on the same page, and different events that then could fire off each of those rules.

    Hope that helps.

  • Does the dataLayer.push code need to appear before or after the GTM snippet?

    For example, if I’m using the following code, should I have our developers place it before or after the GTM snippet


  • Sayf Sharif

    It can appear anywhere on the page. When the event is passed into GTM, you can use it to fire rules off that event. In general though, if you are using an event that does that on page load before the container loads, then you are doing something wron,g as you could just fire the tag on page load instead of the virtualpageview event.

  • Jeffrey

    Thanks for this excellent explanation.
    I’ve set is up as described, but I now have the problem that every pageview is reported double by Analytics. I see the ‘old’ url and my new (virtual) url. What might have gone wrong and how I can I fix this?

  • Joe

    I want to try something that is based on this concept. One of our sites is using css to deliver content in a drop down. It is a Joomla site so I can’t edit the code on the page. I will use a link click listner (old GTM UI)with the rule event equals gtm.Click {{element id}} equals the id. Then create a virtual page view… this is where I get tripped up as your example references a pdf.

    When the drop down happens an id is created for that block of content, should doc path and doc title still be {{element id}}.

    Also, do I need to create a rule/tag for each virtual page view? And, how can I specify that the page view should only be counted 1x per session?

    Lastly… any chance you are going to start re-publishing some of your posts showing the new UI side by side with the old UI. I didn’t think they were that different but they really are!

  • Hello, I successfully added google analtyics to my test site via tag manager, and then did the link click listener tag, but didn’t add the rule to that. I then added the PDF click rule to a PDF pageview like described above. I published this tag but it’s not firing. I didn’t have my developer add anything so that’s why I did link listener. Since I did it this way I don’t have to set up the virtualpageurl and title macros correct? Thanks!

  • Sayf Sharif


    I’m not sure what you’ve done but certainly something not great. If you’re getting a double pageview, you’re doing something that is probably duplicating the basic pageview of the pageload. Make sure that if your’e firing a virtual pageview, that when you fire off whatever rule is creating it that you’re not doing that automatically on the page load, and that the rule requires the event to occur.

    One problem with that might be that you have an all pages rule on the virtual pageview, in addition to one with the GTM event in the rule, causing it to fire automatically on pageload.

    Check your rules, check the values for the page url on the virtual pagehit, etc.

  • Sayf Sharif


    Last things first, it’d be great to go back and redo everything with the new UI, but it’ll depend on time. I agree they’re very different!

    As to your main question, I’d do a few things:

    First, yes you’ll want the event to match the click of the element id of what someone clicks to get that drop down. That way it’ll fire a virtual pageview when someone clicks the link/button/etc.

    Second, if you want it to fire only once on the page, then I recommend that you also set a cookie on that event which just has something like ‘areaviewed=1’ for the session, and then on the original rule to fire the virtual pageview, have a macro value that’s the cookie value, and require it to be not equal to 1. That way if they view it, it sets the cookie to 1, and then when they click it again, the rule won’t match and the vpv will only fire once.

    As far as the naming convention you could either do it based on what they clicked if that is different for each thing, so you can have individual tags for each different thing someone clicks, or you coudl use custom data attributes (like data-page=”pagenamehere”) in the element clicked so that you’d have one tag in GTM. That would require modifying the page however.

    Good luck!

  • Sayf Sharif


    Obviously something went wrong, and it’s hard to say exactly what. The key parts are laid out above, I’d go back and make sure you did everything.

    Make sure the click listener is on every page. That’s probably the biggest thing, make sure all the rules are right, that they’re looking for the right events (capitalization matters) etc. The rules are what are probably preventing the right tag from firing, either they have an error in them, or they’re not applied to the right tags.

  • Sayf:

    I follow the second option (without add in extra datalayer code), and I keep the rule open to “all” clicks, i.e. use one rule “{{event}} contains”.

    I saw the one page-load event, but did not see any click being capture.

    Any advice would be greatly appreciated.

    Duc Haba

  • Sayf Sharif

    Well just making the rule won’t do anything necessarily on the page. What do you have associated with a click event? Another GA event? If you have another tag associated with a general click rule not firing, check to confirm your rule is correct, create a custom html tag with a console.log test note, etc, and confirm that your rules are all actually in place, that the click listener is on the page etc.

    A) the page must have the container code
    B) The page must have a click listener tag on it
    C) A second tag must have a rule with the event of

    If the container is on the page, the pate has a click listener tag being sent to it, and another tag has the event you listed ,it should make it to the page, so probably one of those three things isn’t working right.

  • Chris

    Hi Sayf,

    First thanks for this article it really helps a lot for a new in GTM like me. I’m following the Link Click Listener and I created a goal for a successful click but it didn’t work. Here is what I’ve worked and let me know what is my mistake here.



    Google Analytics Goal:

    Google Analytics Report:


  • Sayf Sharif


    Here’s some possible ways you can test your problem:

    One guess is the rule is bad regarding the element url. Are you sure it’s matching the href or action attribute of the link clicked that triggers the event?

    Are you sure it’s a link you’re clicking on, or maybe there is some other element that might be “stealing the click” from the link itself? Try with a click listener instead of a link click listener, and change the rule to as opposed to gtm.linkClick does that solve the problem?

    Try running the rule with a custom html tag with just a script in there writing to the developer console with a message of the element url so you know you’re clicking on what and that the tag is working.

    Is element url even defined as a macro? in older installs it might not be there, do you need to create the element url macro?

  • @Sayf

    If you had no access to change the code, so pushing datalayers is out :(, how could I track a dynamic content which isn’t a popup as a VPV.

    example, after some form fields you are presented a premium box showing a rate.

    If I wanted to track something like this ie a) premium box showing, b)And each time premium changed.

    I like to give that final state a vpv/premium (this I know how to do..)

    Can you create a listener for if that div was displayed?

    I know they have Click, linkclick,form,custom events like datalayer pushes but this doesn’t fall in that type of trigger.

    As always thank you for amazing articles!

  • I want to do same thing with new version of google tag manager. Please guide me what I do. I don’t find document path and document url option in new version of google tag manager.

    • james

      Me too! I can’t reverse the new version of gtm. Please help!

      • Jon Meck

        Hi Mohit and James,

        The new version makes it a little more complicated. These options are located under “More Settings” and then “Fields to Set.” Instead of Document Path, you would select Page from the list.

  • vladmalik

    Hi Sayf, Can GTM use a page view on a 3rd party domain to trigger a virtual pageview in GA? If I visit 3rd-party-domain/page1, I’d like it to register as a visit to virtual mydomain/page1 on my domain. Thanks.

  • brandon1980

    Great post. I’ve sucessfully set up a virtual pageview in GTM that fires when a form is submitted. I see the virtual page populating in Google Analytics just fine, however, I can’t get the goal to fire in Google Analytics. I’m using the virtual page path of /quick-register.php as the destination URL for the goal. Any help would be mucho appreciated!

    • Sayf Sharif

      What does the page look like IN Google Analytics? Is it just /quick-register.php? If there aren’t any additional error characters (extra quotes, an extra slash, etc) then you should be able to just make a URL Destination goal for the page. If that isn’t working, try doing it as a regular expression on the goal. As far as Google Analytics is concerned it’s like any other page, and doesn’t discern it as “virtual” in any way. Be sure you’re doing it exactly as how the Page shows up in GA. Maybe you’re filtering it, maybe you’re prepending a hostname, etc. Good luck!

      • brandon1980

        Thank you – hostname was prepended in GA – that was the problem – got it fixed! Thank you so much.

  • jkcollier25

    I found all this really
    misleading, but then I found this video

    • Sayf Sharif

      Sorry no intent to mislead. This is now v1 GTM, and there are newer ways to do the above. We’ll need to update this post.

  • Igor

    Hi Sayf!

    Thanks for your post! Have few questions.

    1. What’s the use of the first method you described earlier (initializing virtual page view via datalayer.push)? If there’s quite simple method via “Fields to set”.

    2. At the very beginning, I set up Universal Analytics page view tag to fire on all pages – basic configuration. If I want initialize virtual page views, should I make a new Pageview tag? Or everything is gonna be ok even with one pageview tag.

    • Sayf Sharif

      This is an older post, and there are newer ways to do it now particularly with fields to set in GTM. We need to post an update to this one with newer techniques.

  • aliasgar hirani

    i have a force pop up lead form on my website, it’s like a dialog box that opens & a user can only move forward after filling in the lead. In case of GTM, how do i implement & track the leads coming from my ppc ads & emailer. Thank you.

  • Roman Kaye

    Oct 2015 update: There is no “More settings > Basic configuration”, as in screenshot #4. To fire a virtual pageview, go to “More settings > Fields to set”, set Field Name = page (not {{page}}, just page), Value = /your/page/path.

    • Phil Hill

      If your using universal analytics I think you need to set “location” instead.
      Also there is a trigger now called “Custom Event” This checks the {{event}} variable for a specific value, so you just have to say that the custom event is equal to “VirtualPageview” and then away it will go.

      • Mohammed Gaber

        That is really helped me, I was searching for basic configuration section from while.

        • Will Christensen

          Me too!

  • Igor G

    Thanks for the great article, but I have a question.

    In real world single page app, with multiple ‘pages’, multiple tags per ‘page’, ‘virtual page view’ event would fire lots of times. Thus, the DOM would get polluted and huge pretty quickly. That would lead to memory leaks, slowdowns, and other problems.

    How do you deal with this?

    • Sayf Sharif

      It’s an interesting question. It would really depend on the size and scope of your tracking. You could minimize it by having more generic tags, and then using more creative datalayer pushes so that you wouldn’t be pushing in all that many tags, and would be relying on the dataLayer, or in addition a second tracking object you create, to hold the information for your tracking. That way while you continue on the page, you won’t be adding much additional to the dom, so much as just increasing the size of 1 or 2 data objects on the page?

  • Jon Tucker

    We have (I think) a unique use case of this.

    In short, how can we trigger a virtual page view on the visitor’s behalf while we’re chatting with them on live chat?

    Here’s the situation:

    – We have a live chat conversion happening with a visitor
    – The visitor gives us (through chat) their name, email, budget, blah blah blah – which makes them a “lead” in our business
    – We want to trigger a page view of an existing lead-gen goal in GA as if they had completed the lead-gen form on our site (maybe we’ll create a second chat-specific GA goal, but same challenge).

    We’re using livechatinc and they have a robust API, but I’m not sure it’s a matter of the livechatinc API – I think we need to somehow tell GA to track a virtual page view for this visitor.

    Any idea Sayf?

    * We’re using Google Tag Manager and have full access to coding to be able to hack something together. I just can’t think of how we’d trigger the virtual page view.

    • Phil Hill

      Since your using a third party application, I guess they wont have your GTM script, so you cannot do tracking from their pages. You would need another page that you control and then perhaps create a button which when clicked would send a virtual page view.

  • Mohammed Gaber

    Thanks so much for your article,

    I’m using the new version of google tag manger which is different, I wish if you upgrade article

  • nita123 azhar
Contact Us.

Follow Us



We'll get back to you
in ONE business day.
Our Locations
THE FOUNDRY [map] LunaMetrics

24 S. 18th Street
Suite 100

Pittsburgh, PA 15203


4115 N. Ravenswood
Suite 101
Chicago, IL 60613


2100 Manchester Rd.
Building C, Suite 1750
Wheaton, IL 60187