• Courses
    • Courses
    • Course bundles
  • Blog
  • Resources
    • Youtube channel
    • E-books and Guides
    • GTM Recipes
    • View All Resources
    • GTM Community
    • GA4 community
  • Services
  • About
    • About
    • Contact
  • Login
  • Courses
    • Courses
    • Course bundles
  • Blog
  • Resources
    • Youtube channel
    • E-books and Guides
    • GTM Recipes
    • View All Resources
    • GTM Community
    • GA4 community
  • Services
  • About
    • About
    • Contact
  • Login

March 5, 2025

“Pages per Session” Conversion in Google Analytics 4

Updated: March 5th, 2025

This article was written by Jude Nwachukwu, who was kind to share his tips with Analytics Mania’s readers

Even though I receive many guest post requests every day, 99.9999% of them are just pure trash. And I guess many website owners will relate to this. Some guest posters even offered to write “high quality” articles about cryptocurrencies or dishwashers. But Jude Nwachukwu contacted me with a unique idea tailored to the readers of this blog post. So why not?

You can learn more about Jude and his website DumbData at the end of this blog post. The rest of the text here is Jude’s.

If you are looking for a Single-Page Application solution, read this article instead.

====

It’s no longer news that GA4 replaced Universal Analytics. If you had a Pages per Session goal set up in GA3 and would love to migrate it to your Google Analytics 4 property, keep reading as you’ll learn three methods of setting this up in GA4.

Subscribe and Get the Ebook - working with reports in ga4

Table of Contents

– Hide table of contents –

  • What is the “Pages per session” goal?
  • But how do you set this up in GA4?
  • Method 1: Inside the GA4 user interface
    • How to create a Page Per Session event
    • Creating The Conversion in GA4
    • How would you test this?
    • What Are The Trade-Offs With This Method?
  • Method 2: Using GTM + JavaScript
    • Sending The Event To GA4
    • Creating a conversion in GA4 + debugging
  • Method 3: Custom tag template
    • Import tag template
    • Configure CookieCounter tag
    • Optional: Pushing data to the Data Layer
    • The remaining steps
  • Final words

 

What is the “Pages per session” goal?

If you are familiar with setting up goals in Universal Analytics, then the Pages/Screens per session goal type shouldn’t be a new term for you.

It is one of the signals used to identify that a user is engaged with the content on the website, just like how YouTube uses the number of videos you consume on its platform as one of the ways of measuring how sticky its platform is.

In GA3, it was easy to set up this type of conversion.

All you had to do was select the Pages/Screens per session goal type and then put in a number.

Then, every time a user viewed a specific number of pages or screens (for example, 4), that was counted as a conversion/goal.

 

But how do you set this up in GA4?

There are two methods you can configure Pages/Screens per session goal in Google Analytics 4:

  1. Doing everything in the GA4 UI (this has some trades-off)
  2. Using the JavaScript + GTM (don’t get code phobia, it’s fairly simple)

 

Method 1: Inside the GA4 user interface

You’ll have to leverage one of the GA4 event tracking features called Audience Triggers to do this. Does this ring a bell?

Here is how Google defined audience triggers in their documentation.

 

How to create a Pages Per Session event

You can have this setup in less than 30 seconds.

In this example, we want to count sessions with three or more page views as a conversion, and here is how to do it;

#1. Navigate to the configure tab, and select audiences. Update: now audiences are managed in Admin > Audiences.

#2. Click the New Audience button.

#3. Select Create a custom audience.

#4. Add a title, select the human icon beside the timer icon and change the sequence scoping to within the same session.

#5. Under the condition select page_view event, click Add parameter. Under other, select the Event Count parameter.

#6. For the condition, I’ll leave it as greater than two. Then click Apply.

#7. Change the membership duration to 1 day.

#8. Click Create New under the Audience trigger

#9. Add your desired event name. In this example, au_visited_3_pages will be the event name.

#10. Click Log an additional event when audience membership refreshes and click Save.

Boom, now you’ve created your page-depth event. The next step will be to set this up as conversion in GA4.

Subscribe and Get the Ebook - Server-side tagging

Creating The Conversion in GA4

Navigate to conversions in the configure tab and click the New Conversion Event button.

Update: now conversions are managed at Admin > Conversions.

Enter the event name that you want to treat as a conversion

In this case, it was au_visited_3_pages.

Yay!! We’ve now set up our page depth conversion for sessions with three or more page views.

 

How would you test this?

One of the ways you can verify this is by going to GTM preview mode. Then visit 3 pages, and you should see the au_visited_3_pages event, represented with a green flag, to symbolize that it is a conversion.

If the debug view is not working properly, please refer to this guide.

 

What Are The Trade-Offs With This Method?

  1. You are using the 100 audiences limit in GA4
  2. The membership duration is one day. If a user has two sessions in a day (and both sessions contain 3 or more pageviews), GA4 will track just one conversion instead of two.
  3. The conversion doesn’t count unique page views, only total page views, meaning if the user reloads the same page 3 times, GA4 will count this as a conversion
  4. It looks like events dispatched by an audience trigger are more prone to (not set) traffic source

How can you make GA4 count unique pageviews, not use your audience slots and include all sessions with this conversion? That is where GTM and the power of JavaScript come in handy.

 

Method 2: Using GTM + JavaScript

The first thing we’ll do is to create a Google Tag Manager custom Javascript variable (our variable name is CJS – Page Load Type).

Here is the code to use:

function() {
  try {
    var pageLoadType = performance.navigation.type;
    if (pageLoadType == 1) {
      return "reload"
    }
    else {
      return"unique"
    }
  }
  catch (e) {
    return "unique"
  }
}

You’ll have to add extra configuration to the variable by clicking the format value and setting undefined and null to “unique” (without quotation marks).

This code leverages a browser API called PerformanceNavigation, and its type property (performance.navigation.type).

performance.navigation.type returns a value representing how a page load happened in the user browser.

  • 0 – Represents direct URL entry, clicking a link, and other forms of the normal browser navigation.
  • 1 – The page was refreshed or reloaded.
  • 2 – The page was loaded using the back and forward buttons.
  • 255 – Another navigation method.

We then wrap the browser API in an if-else statement that returns “unique” for all page loads and “viewed before” for refreshes/reloads or pageviews through the backward or forward button.

The next step is to create a page view trigger (“PV – Unique Pageview”) with a trigger, “CJS – Page Load Type”, which equals to “unique”.

To track the number of pages the user has viewed in a session, you need to create a custom HTML tag where the code below will be pasted, and the “PV – Unique Pageview” trigger will be attached to the tag.

<script>
  function gtm_getPageViewCookie(name) {
    var cookies = decodeURIComponent(document.cookie);
    var cookiesArray = cookies.split(';');
    var cookie = cookiesArray.find(function (item) {
      return name == item.trim().substring(0, name.length);
    });
    return cookie;
  }

  function updatePageViewCookie() {
    var cookieName = "gtm_page_view";
    var l = cookieName.length;
    var cookie = gtm_getPageViewCookie(cookieName);
    var visitCount = 0;

    // If cookie exists, set visitCount to value
    if (cookie) visitCount = cookie.trim().slice(l + 1);
    visitCount = parseInt(visitCount) + 1;

    var cookieExpiry = new Date();
    // Set expiry time of 30 mins (in milliseconds)
    cookieExpiry.setTime(cookieExpiry.getTime() + 30 * 60 * 1000);
    var expires = "expires=" + cookieExpiry.toUTCString();

    // Set cookie
    document.cookie = cookieName + "=" + visitCount + ";" + expires + ";path=/";
  }

  // Execute on tag trigger
  updatePageViewCookie();
</script>

The HTML tag name can be called cHTML – Pageview Count (Unique Only).

This code counts the number of pages the user has visited and then stores the value in the cookie with the name gtm_page_view.

The page view count is stored in a cookie that expires in 30 minutes, and its duration is extended to 30 minutes every time a page loads.

The page_view ( “cHTML – Pageview Count (Unique Only)”) count script will only fire on all page loads except for reloads/refreshes making sure we are only counting uniques and not using total.

To get the number of pages the user has seen in that session, you’ll have to create two variables; a 1st-Party Cookie and a Custom JavaScript variable with the name CJS – Page View Number.

For the 1st-Party Cookie, set the cookie name to gtm_page_view, and the variable name must be 1PC gtm_page_view (without any dashes. Ignore the dash in the screenshot).

The next step is to create the custom JavaScript variable using this code;

function() {
  var pageviewnumber = '{{1PC gtm_page_view}}';
  return parseInt(pageviewnumber);
}

The variable helps retrieve the number of pages viewed by that user in that session.

The code accesses the gtm_page_view cookie and returns the value of the number of pages the user viewed in a session as a number data type.

 

Sending The Event To GA4

Since GA4 is an event-based analytics platform, we’ll have to send this conversion first as an event before marking it as a conversion.

To send the event data to GA4, you’ll have to create a GA4 event tag in Google Tag Manager and add the event name.

I’ll use visited_3_pages since we count sessions with three or more page views as a conversion.

After creating the tag, the next step is adding a trigger.

Click on Triggering and then plus sign to add a trigger. Select the Window Loaded trigger and then add a condition where the variable CJS – Page View Number equals 3.

It means that once the user hits the mark, visiting the third page, this event will fire in GTM with the event data sent to Google Analytics.

 

Creating a conversion in GA4 + debugging

From the Conversions screen in the Admin section, click the New Conversion Event button. Enter visited_3_pages as the conversion event.

Hurray!! We’ve now set up our page depth conversion for sessions with three or more page views.

Testing this is easy in GTM preview mode.

Try to visit three pages, but while on the second page, reload the page to check if the GA4 event fired in GTM (it should not because it’s still the second unique page view).

Now visit the third page. The GA4 event should fire in GTM and GA4 debug view, and you should see the visited_3_pages event (with a green flag because it’s marked as a conversion).

The beautiful thing about this approach is that it’s reusable for conversion tracking across other ad platforms like Facebook, Google Ads (if you don’t want to do a GA4 import), TikTok pixel, etc.

 

Method #3. Tag Template Option (No-Code Alternative to the 2nd method)

If you prefer a no-code solution instead of the code-based approach mentioned earlier, you can use a Google Tag Manager (GTM) custom tag template I’ll introduce shortly to track page views or other actions within a session.

However, if you don’t prefer this method, feel free to skip this section and continue with the article.

The tag template you’ll use is “Event/PageView CookieCounter.” You can download it from the GitHub repository.

Please note that using this template does not replace other steps in the implementation process, as it simply assists in counting actions during a session instead of relying on the code used in the GTM Custom HTML tag.

For example, it helps track how many pages a user views within a single session.

 

Importing the Tag Template into Google Tag Manager

After downloading the template, go to the “Templates” tab in your GTM UI. In the Tag Templates section, click the “Add New” button.

On the template creation screen, click the three-dot menu and select “Import.”

Upload the template file you downloaded from GitHub. Once imported, the tag template will be displayed with the brand name “DumbData.” Agree to the terms and conditions, then click “Save.”

 

Configuring the “Event/PageView CookieCounter” Tag

Go to the “Tags” section in your GTM container. Click “Add New” and select the “Event/PageView CookieCounter” tag template you just imported.

Configure the tag by setting the following options:

  • Cookie Name: Enter the cookie’s name that will store the action count (e.g., page view counter).
  • Session Duration: Define the session lifespan based on time units (e.g., minutes).
  • Session Length: Set the duration of a session, such as 30 minutes (or match the session timeout settings in your GA4 property).

 

Optional: Pushing Data to the Data Layer

Enable this option if you want to send a dataLayer event when the tag executes.

This is particularly useful for Single Page Applications (SPAs) where page loads are history changes or your page load dataLayer event.

It can also help push a dataLayer event to show that the cookie value was updated.

  • When enabled, the template pushes a dataLayer event with the default name “gtm_temp_dd_eventUpdate”
  • You can customize the event name by enabling the “Custom Data Layer Event Name” option and entering your preferred event name.

Once you’ve configured the custom tag template, the next step will be to attach the same trigger you would use in the Custom HTML tag approach.

So basically, with the setup, the tag will now count any action you choose, in this case for page views, and count the number of times it happened in a user session, and the session is the cookie duration you’ve configured.

And it does this by;

    • Checking if the action-counting cookie (e.g., page view counter) exists.
    • Incrementing the count by 1 if the cookie is found.
    • Creating the cookie with an initial value of 1 if it does not exist.

Once the tag is configured, the next step will be to create a “1st-party cookie” variable to retrieve the value of the name you’ve assigned to the cookie in the tag template, and the final step is to send the event to your Google Analytics property for reporting, analysis and activation.

 

The Remaining Steps

The next several steps are very similar to Method #2:

  • You’ll need to create a 1st party cookie variable. Its name depends on what you configured in the custom tag (e.g., gtm_pageview_counter)
  • Then create a custom JS variable (it retrieves the number of pageviews stored in a cookie)
  • Create a GA4 event tag (event name can be visited_3_pages or something similar)
  • Create a Window Loaded trigger where you use the Custom JS variable (e.g. “CJS – Page View Number” equals 3)

I will not repeat those steps here once again, you can take a look at Method #2 described in this blog post earlier.

 

Pages per session goal in GA4: Final words

Now you’ve seen 3 different methodologies you can explore when migrating or configuring your Pages/Screens per session goal in your Google Analytics 4 implementation.

Be aware of the trade-offs when implementing inside your GA4 user interface. Now, let me know in the comment what method you’ll be using if you’re to configure yours.

Jude Nwachukwu
Jude is an analytics specialist working with businesses across diverse industries in Africa, Europe, and North America. His passion is to write and assist non-technical marketers to thrive in the ever-evolving realm of analytics. He co-founded a measurement resource hub called DumbData.

 

Jude Nwachukwu
In Google Analytics Tips
19 COMMENTS
Adil
  • Mar 2 2021
  • Reply

Hi Julius,
To your point regarding time based goal conversion [e.g. duration > 5min], would this not be doable by setting a GTM trigger to 300000 [milliseconds] > attaching a GA 4 event to this trigger and then counting that event as a Conversion in GA4 interface?

    Julius
    • Mar 2 2021
    • Reply

    No, because timer trigger resets on every pageview. Timer counts time on page, not the time of session.

      Adil
      • Mar 2 2021
      • Reply

      ah, of course!
      I'm wondering if below could work?

      1. User lands on the site > The *first* Performance.timing.connectStart value extracted [as a JS variable value] and saved to a session cookie after converting UNIX time into regular timestamp.
      2. With each additional page request, the same variable value is checked and new values overwritten into a second var.
      3. Difference between value #1 and #2 timestamps made available as a third JS variable and gets recalculated on each page load.
      4. IF value in third JS variable > 5 min equivalent, fire tag.

        Julius
        • Mar 2 2021
        • Reply

        Yeah, that can work.

        Alexander
        • May 27 2021
        • Reply

        Interesting. Did you manage to work it out?

VA
  • Jan 3 2023
  • Reply

I have the same issue as Sandro. I followed the steps and must have made an error somewhere.

"cHTML - Pageview Count (Unique Only)" - works and fires
- I think that means "PV - Unique Page View" - works as trigger

"GA4 Event - 3 Pages/Session" - does not fire after 3 page loads
- which I think means "Window Loaded - 3rd Page View" does not trigger
- which I think means "CJS - Page View Number" is not counting up. I used "1PC gtm_page_view" for the cookie name and in the code. It confirms that cookie is referenced in the variable.

When I look at variable values in debugging, the cookie and the Page View Number start at 7 and doesn't change as I load additional pages.

This is the code that I copied from you and have in the Pageview Count. I can't see why it starts at 7 and doesn't count up

<script>
function gtm_getCookie(name) {
var cookies = decodeURIComponent(document.cookie);
var cookiesArray = cookies.split(';');
var cookie = cookiesArray.find(function (item) {
return name == item.trim().substring(0, name.length);
});
return cookie;
}

window.onload = function() {
var cookieName = "gtm_page_view";
var l = cookieName.length;
var cookie = gtm_getCookie(cookieName);
var visitCount = 0;

// If cookie exists, set visitCount to value
if (cookie) visitCount = cookie.trim().slice(l + 1);
visitCount = parseInt(visitCount) + 1;

var cookieExpiry = new Date();
// Set expiry time of 30 mins (in milliseconds)
cookieExpiry.setTime(cookieExpiry.getTime() + 30 * 60 * 1000);
var expires = "expires=" + cookieExpiry.toUTCString();

// Set cookie
document.cookie = cookieName + "=" + visitCount + ";" + expires + ";path=/";
};
</script>

Thanks in advance.

VA
  • Jan 3 2023
  • Reply

Followup

I cleared cookies and tried debug view again.

Start / First load showed 1PC gtm_page_view as "undefined" and
CJS Page View Number as "NaN"

Second page load - no change to either variable.

and so on. No change to variables as new windows loaded.

Iulia
  • Jan 9 2023
  • Reply

Hello, I have the same issue as VA, has anyone found a solution? Many thanks.

Jude Nwachukwu
  • Jan 9 2023
  • Reply

Luia and VA, the glitch is from the "CJS – Page Load Type" custom javascript variable.

Here is an updated one that you can use

function() {
try {
var pageLoadType = performance.navigation.type;
if (pageLoadType == 1) {
return "reload"
}

else {
return"unique"
}
}
catch (e) {
return "unique"
}
}

please do let me know the outcome.

I'd also try reaching out to Julius, to see if he can add the update.

    Julius Fedorovicius
    • Jan 10 2023
    • Reply

    Hey, thanks, I have updated the blog post

    Iulia
    • Jan 10 2023
    • Reply

    Hi, Jude, many thanks for your update. After I replaced the script for CJS - Page Load Type, GA4 Event - 3 Pages/Session is firing, but on the fourth page view, not on the third one. I am not sure this is how it is supposed to work, since the triggering condition is CJS Page View Number equals 3 (not 4). Should I do anything else about it? Thanks again.

Kares
  • Jan 11 2023
  • Reply

Hi Julius, Is there any difference between the old UA's “Pages per Session” and the recently added GA4 Views Per Session? Carly Boddy from Google described Views Per Session as "The number of app screens or web pages your users viewed per session. Repeated views of a single page or screen are counted. (screen_view + page_view events) / sessions." Thanks

VA
  • Jan 16 2023
  • Reply

Just updated it - thank you.
It seems to work now.

Steve
  • Feb 17 2023
  • Reply

Thanks for the instructions guys, but I've meticulously followed the steps and it's just not working for me.

I can see my session in Debug. I named everything as you suggested. In fact I built it twice just to make sure I didn't miss anything or have a typo. I can see "page_view" creep up and up as I visit pages.

I created the audience and conversion Exactly as shown, but still no dice.

I'm pretty experienced with GTM, UA etc. I've implemented many of your tips over the years Julius (!! Thanks).

I've reloaded pages and tried everything I can think for method #1 above. I still get 0 for the green flag on conversions.

Any ideas?

Subi
  • Mar 29 2023
  • Reply

Could you explain to me why we have to set the membership duration to 1 day? Wouldn't this mean that the audience would only be populated with last day users?

Mahesha
  • Jun 6 2023
  • Reply

Hi Julius

I applied the modified code and performed the steps. However, my "GA4 Event - 3 Pages/Session" tag is not triggering after three-page loads. Only firing "cHTML - Pageview Count (Unique Only)"

I did everything that was outlined in the blog. can assist me in determining the cause of the problem and its potential location.

Thank you

Ted Jardine
  • Jul 27 2023
  • Reply

To clarify:

<script>
function gtm_getCookie(name) {
var cookies = decodeURIComponent(document.cookie);
var cookiesArray = cookies.split(';');
var cookie = cookiesArray.find(function (item) {
return name == item.trim().substring(0, name.length);
});
return cookie;
}

function iterateUniquePageViewCount() {
var cookieName = 'gtm_page_view';
var viewCount = 0;
var length = cookieName.length;
var cookie = gtm_getCookie(cookieName);

// If cookie exists, set visitCount to value
if (cookie) viewCount = cookie.trim().slice(length + 1);
viewCount = parseInt(viewCount) + 1;

var cookieExpiry = new Date();
// Set expiry time of 30 mins (in milliseconds)
cookieExpiry.setTime(cookieExpiry.getTime() + 30 * 60 * 1000);
var expires = "expires=" + cookieExpiry.toUTCString();

// Set cookie
document.cookie = cookieName + "=" + viewCount + ";" + expires + ";path=/";
}

iterateUniquePageViewCount();
</script>

With this adjustment, you will also need to move the cookie read/write higher in priority as you otherwise might have a race condition with the x number of unique page views trigger (which reads the cookie value, so it should be reading the cookie value *after* the count has been iterated, not potentially before). The easiest solution is to read/write cookie when the DOM is ready (Page View - DOM Ready) vs Page View - Window Loaded.

    Ted Jardine
    • Jul 28 2023
    • Reply

    Huh? My previous comment appears to have disappeared into a black hole.

    I kept running into random timing or function overwriting issues with the unique page view custom HTML (see "cHTML – Pageview Count (Unique Only)" above).

    The custom HTML is emitted via unique page view, in turn, called via GTM’s Page View - Window Loaded event. There is, therefore, no need to assign the function to the problematic window.onload!

    Why is it problematic? Sometimes, especially with some blocking scripts located elsewhere, there can be timing issues. The window.onload = function() was emitted too late (window.onload had already occurred - like registering for a party after it had already taken place - a rather pointless exercise). Or even worse, window.onload could be overwritten by another function assigned elsewhere to window.onload (or overwrite those). Side note: window.onload should be avoided and instead have an event listener, but in either case, the view count would randomly not iterate + 1.

    Since the custom HTML inline script will never be fired earlier than window.onload because it’s in turn called by some GTM’s window load event registration, it’s unnecessary. Moreover, the custom HTML is emitted at the bottom of the page, so it only executes after everything else anyways!

    In other words, just run view count iteration calls inline, and you’ll no longer have random misses on view count iteration (at least those due to this specific issue).

    Happy to hear feedback on any of the above.

Pascal
  • Nov 23 2023
  • Reply

Hi Julius,

Ive followed the method 2 tutorial however, the page count acts a bit random. When I open the website in debug mode it is already set at 3. I tried to change the count to eguals 5 instead of 3, i figured lets add 2 more pageviews however, I then go from 3 to 7 back to 4. Is there a way to fix this?

Thanks in advance.

Leave a comment Cancel reply

Your email address will not be published. Required fields are marked *


 

Hi, I'm Julius Fedorovicius and I'm here to help you learn Google Tag Manager and Google Analytics. Join thousands of other digital marketers and digital analysts in this exciting journey. Read more
Analytics Mania
  • Google Tag Manager Courses
  • Google Tag Manager Recipes
  • Google Tag Manager Resources
  • Google Tag Manager Community
  • Login to courses
Follow Analytics Mania
  • Subscribe to newsletter
Recent Posts
  • Setting up cookie consent for Microsoft Clarity (V2)
  • Conversion rate in Google Analytics 4
  • Google Tag Manager Data Layer Explained
Analytics Mania - Google Tag Manager and Google Analytics Blog | Privacy Policy
Manage Cookie Settings