Key Takeaways
- Bundle size under 20KB compressed. Over 100KB is unacceptable for a consent widget.
- Parse-time or network-level script blocking. Runtime blocking leaves a compliance gap.
- Banner rendered from local template, not remote fetch.
- Hardware-accelerated animations (`transform`, not `bottom`/`top`).
- Gradual script release post-consent, not avalanche.
The Performance Tax: How Bad Is It?
LCP Impact: Your Consent Banner Is the Largest Contentful Paint
OneTrust has been benchmarked extensively. DebugBear measured its impact on LCP: the banner text became the LCP element, jumping load times from 1.43 seconds to 3.61 seconds. A RUMvision case study found the cookie banner was the LCP element for 50% of mobile pageviews, with values hitting 4,721ms.
CookieYes is even worse on mobile: 6.5 seconds for LCP, with the banner as the largest contentful element. Google's "good" threshold is 2.5 seconds.
"The banner adds about 48,000 elements to the DOM. On mobile, the banner is the LCP, with an immense 6.5 seconds." -- stefanchetan, WordPress.org, May 2024
Termly drops WordPress sites scoring 70-74 on PageSpeed to 37-43 with the plugin installed.
"PageSpeed score WITH Termly plugin: 37-43. WITHOUT: 70-74." -- @sriramdev, WordPress.org, June 2024
The pattern is clear: most CMPs don't just affect LCP, they become LCP.
See how the best consent management platforms compare
INP: How Slow Is That "Accept" Button?
DebugBear benchmarked 9 CMPs across 45 test sites:
| Rank | CMP | Median INP |
|---|---|---|
| 1 | Sourcepoint | 6ms |
| 2 | Usercentrics | 56ms |
| 3 | Cookiebot | 57ms |
| 4 | TrustArc | 67ms |
| 5 | Quantcast | 74ms |
| 6 | CookieYes | 81ms |
| 7 | Didomi | 95ms |
| 8 | OneTrust | 104ms |
| 9 | Osano | 275ms |
The spread is 45x between best and worst. Osano's Accept button blocks the main thread for 448ms of CPU processing time. OneTrust's P75 processing time is 113ms on mobile, with only 31% of Accept interactions rating "good" by Core Web Vitals standards.
The consent banner is typically the first interaction a visitor has with your site. If clicking "Accept" produces a visible lag, you've created a poor first impression before the user has seen your content.
DOM Bloat
Google recommends keeping total DOM size under 1,500 elements. CookieYes injects approximately 48,000 DOM elements when its IAB TCF implementation loads. That's 32x Google's recommended maximum. Cookiebot injects 209 DOM nodes, the highest of any CMP in the Agence Web Performance benchmark (average: 84).
A well-built consent banner needs perhaps 20-30 DOM elements.
JavaScript Bundle Size
| CMP | JavaScript Size | Notes |
|---|---|---|
| OneTrust | 184KB+ | Multi-step chain: otSDKStub.js to otBannerSdk.js |
| Usercentrics | Large (async) | Async JS; exact size not independently benchmarked |
| Transcend | 54.3KB compressed | airgap.js core; UI adds 342KB async |
| Cookiebot | 34KB | Synchronous (render-blocking) |
| Ketch | 20.6KB | npm package, minified before gzip |
| ConsentStack | <10KB | Gzipped, zero dependencies, IIFE |
OneTrust's multi-step loading chain (stub, SDK, configuration) involves at least 3 sequential network requests. On a 3G connection with 200ms round-trip time, the chain alone adds 600ms+ before the banner can render.
Caching Failures
Cookiebot has the shortest cache TTL of any CMP benchmarked: 11 minutes. Every returning visitor who comes back after 11 minutes re-downloads the entire script. Best practice for a consent SDK is at least 24 hours.
The Post-Consent Avalanche
The worst damage often happens after the user clicks "Accept All." One SpeedCurve study documented this cascade:
- User clicks "Accept All"
- CMP processes consent and updates storage
- CMP signals consent to GTM via
updateGtmMacros - GTM fires all previously-blocked tags simultaneously
- 73 additional third-party requests load at once
- Page becomes unresponsive during script evaluation
OneTrust's updateGtmMacros function alone takes 190ms of main thread time. Total accept-click processing: ~238ms.
ConsentStack addresses this with gradual script release: blocked scripts are re-injected in a controlled sequence rather than all at once. Learn how to set up Google Consent Mode v2 to handle GTM consent signaling properly.
Why CMPs Are So Heavy
The Platform Problem
Enterprise CMPs didn't start as consent tools. OneTrust is a privacy platform with modules for data mapping, DSAR workflows, and AI governance. The SDK that renders a banner is a delivery mechanism for the platform, not the product itself. Nobody optimized the thing users actually see because the SDK was never the revenue driver.
ConsentStack took the opposite approach: the SDK is the product. A <10KB bundle isn't a marketing constraint. It's a design decision.
Render-Blocking Patterns
Synchronous scripts prevent any content from rendering until they load. Cookiebot's 34KB loads synchronously.
CSS layout animations cause layout thrashing. OneTrust uses the CSS bottom property to animate the banner instead of hardware-accelerated transform: translateY().
Multi-step script chains multiply latency. OneTrust's fetch stub, fetch SDK, fetch configuration sequence involves 3 sequential network requests.
How Parse-Time Script Blocking Changes Everything
The Standard Approach: Runtime Blocking
- Browser begins parsing HTML
- Third-party scripts start loading and executing
- Cookies are set, tracking pixels fire
- CMP script loads (100-200KB+)
- CMP renders the banner
- CMP attempts to retroactively block scripts that already ran
There's a window of non-compliance between page load and CMP initialization. This is why 59% of websites with CMPs still set cookies before consent.
The Parse-Time Approach
ConsentStack installs a MutationObserver during HTML parsing, before any third-party scripts execute:
- ConsentStack SDK loads high in
<head>(<10KB, non-render-blocking) - MutationObserver installed at parse time, watching the DOM
- Any
<script>element is intercepted before the browser executes it - Scripts checked against 6,592 tracker domains (DuckDuckGo Tracker Radar)
- Matched scripts blocked and held in memory
- User makes consent choice
- Approved scripts re-injected; denied scripts never execute
Zero cookies before consent. Zero data leakage.
Why This Matters for Core Web Vitals
No LCP impact. The SDK is <10KB and loads asynchronously. No remote configuration fetch, no multi-step chain.
No CLS. Fixed positioning overlay instead of document flow insertion.
No INP disaster. Consent processed by <10KB script, not a 200KB+ bundle.
No post-consent avalanche. Gradual script release via staggered re-injection.
Learn how parse-time script blocking works
Measuring Your CMP's Performance Impact
Step 1: Baseline Without CMP
Remove your CMP on staging. Run Lighthouse 3 times and average: LCP, INP/TBT, CLS, total JS size, DOM element count.
Step 2: Measure With CMP
Re-add the CMP. Compare deltas. If LCP jumped 500ms+, your CMP is a significant bottleneck. If it added 50KB+ of JavaScript, it's heavier than it needs to be.
Step 3: Real User Monitoring
Check CrUX data via PageSpeed Insights. The critical question: is your consent banner becoming your LCP element?
Step 4: Network Waterfall
A well-designed CMP should add 1-2 requests totaling under 20KB compressed. If you're seeing 4+ requests totaling 200KB+, you have an architectural weight problem.
Step 5: INP Testing
Click Accept, Reject, and Preferences while recording in the Performance tab. Google's threshold is 200ms. Osano's Accept button blocks for 448ms. OneTrust's chain takes ~238ms. Both fail.
The CMP Performance Benchmark
| CMP | SDK Size | LCP Impact | INP (Median) | DOM Nodes | Blocking Method | Cache TTL |
|---|---|---|---|---|---|---|
| ConsentStack | <10KB gzipped | Negligible | <50ms target | Minimal | Parse-time MutationObserver | 24hr+ |
| Sourcepoint | N/A | N/A | 6ms | N/A | Proprietary | N/A |
| Usercentrics | Large | Moderate | 56ms | N/A | Runtime | N/A |
| Cookiebot | 34KB sync | Moderate | 57ms | 209 | Scanner-based | 11 min |
| TrustArc | N/A | 2+ min reported | 67ms | N/A | Runtime + fake delays | N/A |
| Quantcast | N/A | Moderate | 74ms | N/A | Runtime | N/A |
| CookieYes | N/A | 6.5s mobile | 81ms | 48,000 | Runtime | N/A |
| Didomi | N/A | N/A | 95ms | N/A | Runtime | N/A |
| OneTrust | 184KB+ | 1.43s to 3.61s | 104ms | N/A | Runtime | N/A |
| Osano | Small | Low | 275ms (worst) | Low | Runtime | 1 day |
Key takeaways: INP spread is 45x between best and worst. OneTrust ships 184KB+ of JavaScript, over 18x larger than ConsentStack. Cookiebot's 11-minute cache means every returning visitor re-downloads the script.
What to Look for in a Performance-First CMP
- Bundle size under 20KB compressed. Over 100KB is unacceptable for a consent widget.
- Parse-time or network-level script blocking. Runtime blocking leaves a compliance gap.
- No render-blocking scripts.
- Banner rendered from local template, not remote fetch.
- Hardware-accelerated animations (
transform, notbottom/top). - 24+ hour cache TTL.
- Gradual script release post-consent, not avalanche.
- INP under 100ms for all interactions.
ConsentStack was designed against every item on this checklist. <10KB SDK, parse-time MutationObserver blocking, async loading, local template rendering, hardware-accelerated animations, 24-hour+ caching, gradual script release, sub-50ms interaction targets.
Frequently Asked Questions
Yes, directly. Google uses Core Web Vitals (LCP, INP, CLS) as ranking signals. A CMP that pushes LCP from 1.4s to 3.6s (OneTrust, per DebugBear) or injects 48,000 DOM elements (CookieYes) will degrade your scores. Google doesn't penalize you for *having* a consent banner. It penalizes you for having a slow site.
It depends on the CMP. On the light end, less than 10KB and negligible load time. On the heavy end: OneTrust adds 184KB+ and increases LCP by 2.18 seconds. Termly drops WordPress PageSpeed by 30-37 points. CookieYes produces 6.5-second mobile LCP. Osano produces 275ms INP. ConsentStack's <10KB SDK keeps the impact at near-zero.
Yes. GDPR requires collecting consent before tracking, not 200KB of JavaScript. A <10KB SDK with parse-time script blocking achieves the same compliance outcome as a 200KB enterprise SDK. ConsentStack covers 32 regulations with a <10KB bundle. [Read the full GDPR cookie consent requirements](https://consentstack.io/blog/gdpr-cookie-consent-requirements).
ConsentStack at **<10KB gzipped**. Ketch is 20.6KB minified. Cookiebot is 34KB synchronous. Transcend is 54.3KB compressed plus 342KB async UI. OneTrust is 184KB+. Both size and loading strategy matter.
Neither is ideal. `async` may interrupt rendering. `defer` waits until parsing is complete, creating a compliance window. The best approach is a CMP small enough that the strategy barely matters. ConsentStack's <10KB SDK installs the MutationObserver before other scripts can execute, closing the compliance window without blocking rendering. ---
Conclusion
Consent management is a legal requirement. Performance destruction is not. Nothing in any privacy regulation says you need 200KB of JavaScript to ask someone if they accept cookies.
ConsentStack was built to prove consent management and web performance aren't in conflict. <10KB gzipped. Parse-time MutationObserver script blocking. Zero tracking before consent. 32 regulations. $29/month.
Your Lighthouse scores earned every point through careful optimization. Your consent banner shouldn't take them away.