# How to track link clicks in GA4 with GTM (outbound, mailto, tel, downloads)

**Author:** Piotr Litwa - GTM & Analytics Specialist  
**Published:** 2026-08-14  
**URL:** https://piotrlitwa.com/articles/en/track-link-clicks-ga4-gtm.html  
**Language:** en  
**Keywords:** track link clicks ga4, gtm link click trigger, ga4 outbound clicks, click url variable, track mailto clicks ga4

---

To track link clicks in GA4, you've got two routes: turn on enhanced measurement, which sends a `click` event for outbound links and `file_download` for files, or build a **Click - Just Links** trigger in Google Tag Manager and send your own event with the parameters you choose. Enhanced measurement covers the basics for free. GTM covers everything it misses: internal CTAs, `mailto:` and `tel:` links, button labels, and where on the page the click happened.

The trick is knowing where one ends and the other begins. Get it wrong and you'll either miss half your clicks or count the same click twice, in two different event names, forever.

> **Key Takeaways**
> - Enhanced measurement sends `click` (with `outbound`, `link_url`, `link_domain`) only for links to other domains. Internal links and most `mailto:` / `tel:` links are invisible to it.
> - For anything else, use Triggers > New > Click - Just Links in GTM, plus the built-in Click URL and Click Text variables.
> - Don't name your GTM event `click` or `file_download`. Google lists both as reserved event names for web. Use something like `link_click` or `contact_click`.
> - If GTM and enhanced measurement both track outbound links, you get two events per click. Pick one owner per link type.
> - Test in GTM Preview and GA4 DebugView, then register custom parameters as custom dimensions, or they won't show up in reports.

## What GA4 tracks on its own (and what it quietly skips)

Enhanced measurement lives in Admin > Data streams > your web stream > Enhanced measurement (the gear icon). Two toggles matter here.

**Outbound clicks** fires the `click` event "each time a user clicks a link that leads away from the current domain", in Google's words. It sends `link_classes`, `link_domain`, `link_id`, `link_url` and `outbound` (a boolean). Links to domains you've listed for cross-domain measurement don't count as outbound. That's by design.

**File downloads** fires `file_download` when the link points to a file matching Google's extension regex: `pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma`. Parameters: `file_extension`, `file_name`, `link_text`, `link_url`, plus classes and ID.

Notice what's missing. No internal links. No "Book a call" button that goes to `/contact`. No `.webp` or `.json` downloads.

And in my experience, `mailto:` and `tel:` links don't show up as outbound clicks reliably enough to report on. I don't trust enhanced measurement for contact links at all.

Here's the comparison I give clients:

| What you want to track | Enhanced measurement | GTM Just Links trigger |
|---|---|---|
| Outbound links to other domains | Yes, `click` event | Yes, if you build it |
| Internal CTA links (`/pricing`, `/contact`) | No | Yes |
| `mailto:` and `tel:` links | Don't rely on it | Yes |
| PDF, DOCX, ZIP downloads | Yes, `file_download` | Yes |
| Unusual file types (`.webp`, `.json`, `.ics`) | No | Yes, with your own regex |
| Link text as a parameter | Only for downloads | Yes, via Click Text |
| Where on the page (header, footer, article) | No | Yes, with a small extra variable |
| Effort | One toggle | 20-40 minutes per setup |

For a small brochure site with five outbound partner links, the toggle is fine. For anything where a click is a lead signal, I build it in GTM.

## Why I stopped trusting the toggle for contact links

Paulina runs a physiotherapy clinic chain with four locations around Poznań. Her agency reported "phone clicks" from GA4 for over a year, and the number looked healthy: 310 a month. Then the clinic switched call tracking providers and the call log showed 1,140 calls started from the website in the same month.

The GA4 number wasn't phone clicks. It was the `click` event, filtered by "contains tel" on a dimension that only some of the clicks carried. On mobile Safari, a chunk of `tel:` clicks never produced an event at all. Nobody had checked, because the number moved up and down like a real metric.

I rebuilt it in GTM in one afternoon: one Just Links trigger for `tel:`, one for `mailto:`, one GA4 event tag called `contact_click` with a `contact_method` parameter. The next month GA4 showed 1,062 `contact_click` events with `contact_method = phone`. Not 1,140, because some people dialed the number by hand after reading it. Close enough to plan budgets on.

That's the lesson. A plausible number is more dangerous than a missing one.

## How to track link clicks in GA4 with GTM, step by step

This assumes you already have a Google tag (your GA4 measurement ID) in the container. If GTM itself is new to you, start with [what Google Tag Manager is and how it works](https://piotrlitwa.com/articles/en/what-is-google-tag-manager.html), then come back.

### Step 1: Turn on the built-in Click variables

Go to Variables > Built-In Variables > Configure, and tick everything in the **Clicks** section: Click Element, Click Classes, Click ID, Click Target, Click URL, Click Text. They're off by default in a new container. If you skip this, your trigger condition dropdown won't even show Click URL.

### Step 2: Create a hostname variable for the clicked link

Click URL gives you the full `href`. For outbound logic, you want just the hostname.

Variables > User-Defined Variables > New > URL. Set **Component Type** to Host Name, tick **Strip 'www.'**, and under More Settings change **URL Source** from Page URL to `{{Click URL}}`. Name it `URL - Click Hostname`.

This one variable saves you from fragile "Click URL does not contain mysite.com" conditions that break the day someone links to `mysite.com.evil-redirect.net`. Hostname matching is exact, substring matching is not.

### Step 3: Build the trigger

Triggers > New > Trigger Configuration > **Click - Just Links**. You'll see two checkboxes:

- **Wait for Tags**: GTM holds the navigation until your tags fire or the timeout passes. Default max wait is 2000 milliseconds. It only matters for links opening in the same tab.
- **Check Validation**: the trigger fires only when the click is a "valid action", meaning nothing else on the page cancelled the default link behavior. Leave it unticked if your site uses JavaScript routers or `preventDefault()` on links, or the trigger will never fire.

If you tick either, GTM asks **Enable this trigger when**. Set it to `Page URL matches RegEx .*` unless you want to limit it.

Then **This trigger fires on: Some Link Clicks**, with conditions. Three triggers I use on almost every site:

1. **Outbound**: `URL - Click Hostname` does not match RegEx `(^|\.)example\.com$` AND Click URL starts with `http`
2. **Phone**: Click URL starts with `tel:`
3. **Email**: Click URL starts with `mailto:`

### Step 4: Create the GA4 event tag

Tags > New > Tag Configuration > Google Analytics > **Google Analytics: GA4 Event**. Fill in your measurement ID, then:

- **Event Name**: `link_click` for outbound, `contact_click` for phone and email
- **Event Parameters**:
  - `link_url` = `{{Click URL}}`
  - `link_text` = `{{Click Text}}`
  - `link_domain` = `{{URL - Click Hostname}}`
  - `contact_method` = `phone` or `email` (on the contact tag only)

Attach the matching trigger. One tag per event name keeps reports clean. I'd rather have three small tags than one clever tag with a Lookup Table nobody understands six months later.

### Step 5: Preview, test, publish

Click Preview, open your site, click a link. More on testing below, because this is where most setups fail quietly.

## Tracking mailto, tel and downloads without the mess

Contact links deserve their own event. They're not "outbound traffic", they're intent. If you lump `tel:` into a generic `link_click`, someone will build a report on outbound partner clicks and accidentally include 400 phone calls.

A few specifics that bite people:

- **Click Text on icon links is empty.** A phone icon with no visible text gives you `link_text = ""`. Add a `link_location` parameter instead, built from a DOM attribute like `data-location="header"` or from the closest section. A Custom JavaScript variable reading `{{Click Element}}.closest('[data-location]')` works if devs add the attribute.
- **Downloads beyond the default list.** If you host `.ics` calendar files or `.webp` assets people actually save, build a Just Links trigger with Click URL matching RegEx `\.(ics|webp|json)(\?|$)` and send `file_download_custom`, not `file_download`.
- **Parameter values cap at 100 characters.** Google's limit for event parameter values on standard properties is 100 characters. A long signed download URL gets cut. Strip query strings with a URL variable if they're noise.

```js
// Custom JavaScript variable: link location from the nearest data attribute
function() {
  var el = {{Click Element}};
  if (!el || !el.closest) return 'unknown';
  var box = el.closest('[data-location]');
  return box ? box.getAttribute('data-location') : 'body';
}
```

## Avoiding double counting with enhanced measurement

Marcin manages marketing for a SaaS reseller in Kraków. His Looker Studio report showed partner link clicks jumping from roughly 900 to 1,850 a month, overnight, right after a freelancer "added proper link tracking in GTM". Partners were thrilled. Marcin wasn't, because demo requests didn't move at all.

Both systems were firing. Enhanced measurement sent `click` with `outbound = true`. The new GTM tag sent `outbound_click` for the same links.

The Looker report summed both event names because the filter was "event name contains click". Every outbound click counted twice.

Two clean ways out:

1. **GTM owns outbound links.** Turn off the Outbound clicks toggle in enhanced measurement. Your GTM tag becomes the single source.
2. **Enhanced measurement owns outbound, GTM owns the rest.** Keep the toggle, and scope your GTM triggers only to internal CTAs, `tel:` and `mailto:`.

I'll be blunt: option 1 is almost always better. You control the parameters, you can add `link_location`, and you can see the logic in a container version history. A toggle in a GA4 admin screen gives you none of that.

And never name your GTM event `click`. Google's event naming rules list `click`, `file_download`, `scroll`, `video_start` and `video_progress` as reserved names for web. Even if a hit gets through, you've mixed two sources into one event name and you'll never untangle them in reports.

## Testing in Preview and DebugView

Preview mode first. Click a link on your site, then look at the Tag Assistant panel on the left. You should see a **Link Click** event (the data layer name is `gtm.linkClick`). Select it and check:

- **Tags tab**: your `GA4 - link_click` tag under "Tags Fired". If it sits under "Tags Not Fired", open it and see which trigger condition failed.
- **Variables tab**: Click URL, Click Text and your hostname variable hold what you expect. An empty Click Text here means an empty parameter in GA4.

A classic trap: links that open in a new tab with `target="_blank"` are easy to test. Same-tab links unload the page before you can look. Hold Ctrl or Cmd when clicking, or tick Wait for Tags while testing.

Then open GA4 Admin > DebugView. Your `link_click` event should appear within seconds, with its parameters listed.

If it's in Preview but not DebugView, check consent. With `analytics_storage` denied, what reaches GA4 depends on your consent mode setup. My [GA4 debug mode guide](https://piotrlitwa.com/articles/en/ga4-debug-mode.html) covers the cases where DebugView stays empty for no obvious reason.

## Registering parameters as custom dimensions

Your event will show up in Reports > Engagement > Events within 24-48 hours. The parameters won't, unless they map to a dimension GA4 already has.

GA4 ships predefined dimensions for enhanced measurement parameters: Link URL, Link domain, Link text, Outbound, File name, File extension. Your own parameters, like `link_location` or `contact_method`, need registering. Admin > Data display > Custom definitions > Create custom dimension, scope **Event**, and type the exact parameter name.

Three rules I follow:

- Register only what someone will filter by. A standard property gets 50 event-scoped custom dimensions, and they go faster than you think.
- Data appears from the moment you register, not retroactively. Register on day one.
- Match names exactly. `Link_Location` and `link_location` are two different things to GA4.

The full walkthrough, including the limits per property type, is in the [GA4 custom dimensions guide](https://piotrlitwa.com/articles/en/ga4-custom-dimensions.html). If your link data should come from the site itself rather than DOM scraping, the [GA4 data layer and GTM guide](https://piotrlitwa.com/articles/en/gtm-data-layer-ga4-guide.html) shows how to push it cleanly.

## Frequently asked questions

**Does GA4 track link clicks automatically?**
Only outbound ones, through enhanced measurement, as a `click` event with `outbound`, `link_url` and `link_domain`. Internal links and CTAs aren't tracked at all. For those you need a GTM trigger or custom code.

**What's the difference between Click - All Elements and Click - Just Links?**
Just Links listens only to clicks on `<a>` elements and returns the link's `href` as Click URL, even if the user clicked a `<span>` inside it. All Elements fires on any click and returns details of the exact element clicked. For links, Just Links gives you cleaner data.

**How do I track mailto and tel clicks in GA4?**
Create a Just Links trigger with Click URL starting with `mailto:` or `tel:`, then a GA4 Event tag with a custom name like `contact_click`. Add a `contact_method` parameter and register it as a custom dimension so you can split email from phone.

**Why does my link click tag fire twice?**
Usually either enhanced measurement and GTM both track the same links, or two triggers match the same click. Check Preview for two tags firing on one Link Click event, and check whether the Outbound clicks toggle is still on.

**Should I tick Wait for Tags?**
For same-tab links that leave your site, yes, it gives the hit time to send before the page unloads. The downside is up to 2 seconds of delay if something hangs. For new-tab links it does nothing useful.

**Can I name my GTM event "click"?**
I wouldn't. Google lists `click` as a reserved event name for web streams. Use a name of your own like `link_click`, keep it lowercase with underscores, and stay under 40 characters.

## Next steps

Open your GA4 property today and check two things: is the Outbound clicks toggle on, and does any GTM tag also track outbound links? If both are true, you're counting twice. If neither covers `tel:` and `mailto:`, you're missing your highest-intent clicks entirely.

Fix the ownership question first. Then build the three triggers above, test them in Preview, and register `contact_method` before you need it.

If you'd rather have someone go through the container and tell you exactly which clicks are double counted, which are missing, and which "phone clicks" metric is fiction, the [free GTM audit](https://piotrlitwa.com/checkGTM/) does that. For ongoing checks that catch it when a redesign breaks your triggers, see the [GTM monitoring retainer](https://piotrlitwa.com/services.html#gtm).

---

**Sources and further reading**
- [Enhanced measurement events](https://support.google.com/analytics/answer/9216061) (Google Analytics Help)
- [Click trigger](https://support.google.com/tagmanager/answer/7679320) (Tag Manager Help)
- [Built-in variables for web containers](https://support.google.com/tagmanager/answer/7182738) (Tag Manager Help)
- [Event naming rules](https://support.google.com/analytics/answer/13316687) (Google Analytics Help)

---

*Written by [Piotr Litwa](https://piotrlitwa.com/about.html) - independent GTM & Analytics specialist.*
