← All resources Tutorial

Content groups in GA4: setting them up with Google Tag Manager

A content group is a bucket of pages that belong together: all product pages of one category, every blog post about one theme, the support section, the checkout. GA4 reports on single pages by default, which is fine for a site with twenty pages and useless for one with two thousand. With content groups you see how a whole section performs and compare sections with each other: does the men's collection get more engaged visits than the women's, do knowledge base readers come back, which part of the blog brings in leads.

GA4 has a built-in place for this: the content_group parameter. Send it with your page views and events and GA4 fills its own Content group dimension. This guide sets it up with Google Tag Manager in four steps. Part of the GA4 setup guide.

Step 0: decide on your groups first

Every site is different, so the groups come from your structure, not from a template. A few patterns that work:

Two rules keep the data useful. Have fewer than ten or so groups, so a report stays readable. And make sure your URLs actually reveal the group: the setup below reads the page path, so /men/shirts/... is easy to group and /p/18342 is not. If your URLs carry no structure, use a data layer value from your CMS instead (see the FAQ).

Step 1: a RegEx Table variable in Tag Manager

In Tag Manager, go to Variables › User-Defined Variables › New, choose RegEx Table (under Utilities) and name it RegEx Table · Content group.

  1. Input variable: {{Page Path}}. The path is the part of the URL after the domain, so the same table works on every domain and environment.
  2. Add a row per group. The pattern is a regular expression, the output is the group name. For example, pattern ^/men/ with output Men, and ^/blog/|^/news/ with output Blog. The pipe means "or".
  3. Set Default Value to Other, so pages that match no row still get a group instead of an empty value.
  4. Under Advanced Settings, uncheck Full Matches Only (a pattern only has to occur in the path, not match all of it) and leave Enable Capture Groups and Replace Functionality off.

The table is read top to bottom and stops at the first match, so put specific rows above general ones: ^/blog/ga4/ before ^/blog/. Keep patterns simple and test them on regex101.com before you paste them in; a typo in a regular expression fails silently and puts everything in Other.

Step 2: send content_group with every event

The cleanest way to attach a parameter to everything GA4 receives is a shared settings variable, so you define it once instead of in every tag.

  1. Create a variable of type Google Tag: Event Settings, name it GA4 · Event settings, and add a row: parameter content_group, value {{RegEx Table · Content group}}.
  2. Open your Google tag (the one with your G- measurement ID). Under Shared event settings, select {{GA4 · Event settings}}. Page views now carry the group.
  3. Open each Google Analytics: GA4 Event tag and select the same variable under Event Settings Variable. Clicks, form submissions and purchases now carry the group too, so you can see which section a conversion happened in.

On an older container you may still see a "GA4 Configuration" tag with a Fields to Set table. It works the same way: add content_group there and as an event parameter on each event tag.

Step 3: test before you publish

Open Preview in Tag Manager, click through pages from different groups and check the variable's value on each page in the Variables tab. Then open DebugView in GA4 (Admin › Data display › DebugView), click a page_view event and look for content_group among its parameters. Check a page that should fall into Other as well: if it lands in a real group, a pattern is too broad.

Publish only when every group comes through as intended. Content groups are not applied to data that was already collected, so a mistake that runs for a month stays in that month.

Step 4: where the data shows up

content_group is a parameter GA4 recognizes, so there is nothing to register. It fills the built-in Content group dimension, which you find in:

Because the group travels with events too, a filter such as "key events where Content group is Blog" tells you how much of your conversion volume starts in your content, not just how many people read it.

More than one way of grouping

GA4 has one built-in content group, unlike Universal Analytics, which had five. If you want a second grouping (say "page type": product, category, article, landing page next to your category groups), build a second RegEx Table, send it as its own parameter such as page_type in the same Event Settings variable, and register it as an event-scoped custom dimension under Admin › Data display › Custom definitions. Custom dimensions only fill from the moment you register them, so register first, then publish.

A grouping for language is a special case with its own article: the best GA4 setup for multilingual websites.

When you run GA4 through a server container

Nothing changes in the setup. content_group is set in the browser and travels to your server container with each hit, so it reaches GA4 unchanged. It is also available to every other tag in the server container: you can send the same group to Meta or Google Ads as a custom parameter, or use it in a server-side trigger, without building it twice. And because hits go to your own domain, visitors with an ad blocker are grouped too, which makes the section comparison more complete.

Frequently asked questions

Do I need a custom dimension for content_group?

No. GA4 recognizes content_group and fills its built-in Content group dimension on its own. You only need a custom dimension for extra groupings with your own parameter names.

Do content groups apply to my historical data?

No. A group is attached to events at the moment they are collected. Pages visited before you published the setup have no content group, and changing a pattern later does not regroup older data.

My URLs do not show the category. What now?

Read the group from the page instead of the URL. Most CMSs and shop platforms can push the category or template name into the data layer; a Data Layer Variable then replaces {{Page Path}} as the input, or becomes the value of content_group directly.

Why does everything end up in Other?

Usually a pattern that never matches: a missing slash, an escaped character that should not be, or Full Matches Only still checked. Test the patterns on regex101 against real paths from your Pages report and check the variable in Tag Manager Preview.

Can I set content groups in GA4 without Tag Manager?

Yes. With gtag.js you pass content_group in the config call or with each event. GA4 itself has no interface for defining groups by URL; the value always has to arrive with the event.

Continue reading

Ready to improve your tracking setup?

Start free, no credit card required. Live in about 15 minutes.

Start free