Custom HTML in GTM Containers: What 28,000 Tags Reveal
89% of B2B SaaS GTM containers have Custom HTML tags. The median container has 8. The mean is 14.3. One container in the dataset has 148.
Custom HTML is the escape hatch. When a vendor doesn't have a native GTM integration, when a tool needs JavaScript to run, when someone has a snippet from a vendor email, it goes into Custom HTML. The code runs with full page access, no sandboxing, and in 80% of cases, no consent configured.
We analyzed 28,546 Custom HTML tags across ~2,000 B2B SaaS GTM containers as part of our State of GTM research. Here's what's inside them.
Custom HTML count and container health
Custom HTML count is a stronger predictor of health score than raw tag count. The more Custom HTML tags in a container, the lower it scores.
| Custom HTML tags | Containers | Mean score |
|---|---|---|
| 0 | 214 | 90.6 |
| 1-5 | 558 | 81.2 |
| 6-10 | 358 | 75.6 |
| 11-20 | 424 | 72.2 |
| 21-50 | 338 | 69.6 |
| 51+ | 98 | 67.3 |
A 23-point drop from zero Custom HTML to 51+. Containers without any Custom HTML average an A grade (90.6). Containers with 51+ average a C (67.3).
The reason is that Custom HTML tags concentrate findings: consent gaps (80% at NOT_SET), obfuscated code (19% unreadable), replaceable implementations (49% have native alternatives), and duplicate script loading (23%). Custom HTML represents about 22% of all tags in the median container but generates a disproportionate share of audit findings.
What 28,546 Custom HTML tags do
We classified code patterns across every Custom HTML tag in the dataset. Tags can match multiple patterns.
| Pattern | Tags | What it means |
|---|---|---|
| Loads external scripts | 11,174 | Creates a <script> element and injects it into the page |
| Manipulates DOM | 6,525 | Modifies page elements, injects pixels, adds tracking elements |
| Pushes to dataLayer | 3,364 | Enriches GTM data for other tags to use |
| Adds event listeners | 1,913 | Listens for clicks, scrolls, form submissions |
| Reads cookies | 1,458 | Accesses document.cookie for session data, tracking IDs |
| Uses jQuery | 459 | Depends on jQuery being loaded |
| Uses document.write | 62 | Legacy pattern that blocks page rendering |
| Uses btoa | 15 | Base64 encoding, sometimes data transformation |
| Uses eval() | 4 | Executes arbitrary code strings |
11,174 tags that bypass tag management
The most common pattern, and the most notable. 11,174 Custom HTML tags load external JavaScript files by injecting <script> elements into the page. Each one bypasses the core purpose of a tag manager: controlled, auditable, consent-managed tag deployment.
The most common scripts loaded this way:
- Meta Pixel (
connect.facebook.net/fbevents.js), 1,102 instances - LinkedIn (
window.lintrkinitialization), 1,430 instances - Facebook SDK (
www.facebook.comreferences), 1,091 instances
These are ad platform scripts with native GTM alternatives. They're in Custom HTML because they were pasted in before the alternatives existed, or because the person configuring GTM was given a code snippet by the vendor and didn't check the Template Gallery.
1,458 tags reading cookies
Cookie access in Custom HTML is common and usually legitimate: reading UTM parameters, session IDs, or user preferences. But cookie reads also mean the tag has access to authentication cookies, session tokens, and any other data stored in document.cookie. In a container where obfuscated vendor code also reads cookies, that's code you can't read accessing data you can't audit.
459 tags using jQuery
459 Custom HTML tags depend on jQuery, a 90KB library that dominated web development from 2006 to 2015. Most of what jQuery does (DOM selection, event handling, AJAX requests) is available natively in modern browsers. Loading jQuery through GTM adds weight to every page load and creates a dependency that breaks if jQuery isn't available or loads in the wrong order.
13% of containers have the jquery-dependency finding.
19% contain code nobody can read
372 containers (19%) have Custom HTML tags with obfuscated or heavily minified code. These are vendor-provided snippets pasted into GTM verbatim: compressed, variable-renamed, and impossible to audit without deobfuscation tools.
Obfuscation isn't inherently malicious. Vendors minify code to reduce file size and protect intellectual property. The issue is that nobody in the organization can verify what the code does. It reads cookies, manipulates the DOM, loads external scripts, and you have to trust it because the source isn't readable. A typical obfuscated tag has variable names reduced to single letters, string literals encoded, and control flow flattened. It runs. It does something. Nobody has opened it since the initial paste.
The vendor template gap
Nearly half the containers in the dataset have Custom HTML tags that could be replaced with native GTM tag types or community templates.
| Platform | Custom HTML tags | Alternative | Current adoption |
|---|---|---|---|
| Meta/Facebook | 2,903 | Community template | 40% template, 60% Custom HTML |
| 1,552 | Native GTM tag type | 35% native, 65% Custom HTML | |
| Microsoft Ads | (all native) | Native GTM tag type | 100% native |
| 278 | No alternative | 100% Custom HTML | |
| Twitter/X | 180 | No alternative | 100% Custom HTML |
Meta and LinkedIn are the biggest migration opportunities. Both have GTM-native alternatives that offer structured configuration, consent integration, and sandboxed execution. But 60% of Meta implementations and 65% of LinkedIn implementations remain as Custom HTML.
Microsoft Ads is the reference case: 100% native adoption, likely because UET has been a built-in GTM tag type for years rather than a community gallery addition.
Reddit and Twitter/X have no native GTM integration at all. Custom HTML is the only option. Every Reddit and Twitter tag in the dataset loads third-party JavaScript with no sandboxing and minimal consent configuration.
49% of containers (977) trigger our replaceable-custom-html finding, meaning they have at least one Custom HTML tag where a native or community template alternative exists. Simo Ahava's guidance on this is direct: Custom HTML should be used "only when absolutely necessary."
Custom HTML and consent
The consent picture for Custom HTML is documented in detail in our consent deep dive. The summary:
- Native Google tags: 100% have consent configured (template enforces it)
- Custom HTML tags: ~20% have consent configured (defaults to NOT_SET)
- Universal Analytics tags: ~5% have consent configured
80% of 28,546 Custom HTML tags remain at NOT_SET. There's no template enforcement, no validation step, no warning in the GTM interface beyond a badge at publish time. The consent settings section exists on every Custom HTML tag; it rarely gets configured.
41% of containers have Custom HTML containing known ad pixel code (Meta fbq(), LinkedIn lintrk(), TikTok ttq(), Reddit rdt(), Twitter twq()) with no consent configured in GTM. These are the tags transmitting data to third-party ad platforms with no consent gate inside the container.
Having a CMP on the page doesn't resolve this at the GTM level. 46% of containers with Custom HTML also have a CMP, but the CMP manages consent at the page level while the Custom HTML tag's GTM consent setting is a separate, unconfigured layer. Whether the page-level CMP blocks the tag depends on the CMP's implementation (enterprise CMPs like OneTrust typically intercept script loading; simpler CMPs may not).
Custom HTML by vertical
| Vertical | Mean Custom HTML | n |
|---|---|---|
| HR | 28.5 | 48 |
| Collaboration | 26.5 | 42 |
| SEO | 25.0 | 27 |
| Fintech | 21.9 | 45 |
| Cybersecurity | 20.8 | 62 |
| Analytics | 20.5 | 53 |
| Martech | 19.2 | 73 |
| DevTools | 16.3 | 67 |
| Design | 11.2 | 18 |
| Consent & privacy | 8.4 | 16 |
HR and collaboration tools average 27-29 Custom HTML tags per container, nearly double the dataset mean. These verticals tend to run complex marketing stacks with many tools that lack native GTM integrations: ABM platforms, chat widgets, form handlers, enrichment tools.
Consent and privacy vendors have the least Custom HTML (8.4 per container). They run tighter containers and, presumably, practice what they sell.
The security surface
Custom HTML creates a security surface that grows with every tag added.
| Finding | Containers | Share |
|---|---|---|
| Duplicate external scripts | 464 | 23% |
| Obfuscated vendor code | 372 | 19% |
| jQuery dependency | 259 | 13% |
| Global variable pollution | 167 | 8% |
| innerHTML usage | 90 | 5% |
| document.write | 29 | 2% |
| eval() | 4 | <1% |
23% of containers load the same external script from multiple Custom HTML tags, paying the network and execution cost twice. 19% have code that can't be reviewed through static analysis. 13% depend on jQuery.
The 4 eval() instances are the most notable from a security perspective. eval() executes arbitrary code strings and is flagged by CSP policies, linting rules, and security scanners universally. Its presence in a GTM container means someone pasted vendor code containing eval without review.
What to check in your container
Count your Custom HTML tags. Containers with more than 20 Custom HTML tags likely have tags that could be replaced with native alternatives. The score data shows a meaningful drop above that threshold.
Check for vendor templates. Meta Pixel, LinkedIn, TikTok, and several other platforms have GTM community templates or native tag types. If you're running their code in Custom HTML, check whether a template exists in the GTM Template Gallery.
Audit consent on Custom HTML. Every Custom HTML tag has a consent settings section in GTM. If it says NOT_SET, decide whether it should have consent configured. Ad pixels and third-party tracking scripts almost certainly should.
Review obfuscated code. If you have Custom HTML tags you can't read, find out what vendor provided the code and whether they offer documentation or a template alternative. Unknown code reading cookies and loading external scripts is a trust decision.
Remove jQuery dependencies. If your Custom HTML loads or depends on jQuery, check whether the code can use native browser APIs instead. document.querySelector replaces $(), fetch replaces $.ajax, and addEventListener replaces $.on.
Companion to the State of GTM in B2B SaaS. ~2,000 GTM containers. 28,546 Custom HTML tags analyzed. April 2026. Scanning engine: TagManifest.