A topic I’ve covered many times already in LinkedIn posts and both in-house and public talks: the performance impact caused by consent management platform (CMP) scripts.
And recently, I got a new web performance case where their Consent Management Platform (CMP) was one of the main bottlenecks: Optanon (acquired by OneTrust).
The OneTrust Consent script frequently becomes a web-performance and responsiveness issue, and in multiple ways.
Largest Contentful Paint
Any third-party script loaded from a different domain can become a loading-speed issue. For example if itâââ‰â¢s included as a render-blocking resource in the head section of your page. When injected without async or defer attribute, it will block parse and render process in the browser. This will push back both the First Contentful Paint (FCP) and Largest Contentful Paint (LCP) metric.
- FCP is the moment that a user will feel: the page remains white while the browser is stuck downloading and processing resources in the head.
- LCP is part of Google's set of Core Web Vitals and can impact both SEO and even SEA via the Quality score. While Google is only using LCP data coming from Google Chrome usage, other browsers like FireFox and Safari started to support LCP metric.
The Cookie Consent dialog gap
To reduce the impact of a cookie-consent script, I advise site owners to add the async attribute. As a result, more important resources like 1st party stylesheets and scripts are allowed to be downloaded and executed sooner. Important functionality like a hamburger menu that will respond to user interaction will then already be available.
However, loading a cookie-consent script at a lower priority has a trade-off: the interval between First Contentful Paint (FCP) and the cookie-dialog rendering will grow. And this could hurt a website's LCP score even more when the cookie dialog becomes your LCP. This could happen when the cookie dialog text is bigger than any other 1st party text-, image- or video node.
Fetchpriority to the rescue
That's why I advice site owners to use the fetchpriority attribute as explained in this LinkedIn post. The fetchpriority attribute is already supported in all major browsers.
Homework for CMPs
There's still a role for CMPs as well to allow better LCP tracking while still having visibility into the moment that a dialog was rendered.
Knowing this will help site owners to know the gap between moment of FCP (when the page became visible) and when the dialog was rendered. With this information, site owners can reliably experiment with different ways of serving the cookie script: async, defer and with or without the fetchpriority attribute.
LCP candidate
CMPs should not stuff all text within a single div element. Instead, they should split text into multipe paragraphs when the cookie text contains line breaks.
Consent Dialog element
CMPs should add elementtiming to their cookie dialog. Although container timing API is on its way, it's not supported yet, meaning that the elementtiming API should be used instead. While elementtiming is not supported in all browsers yet, being able to collect such data from supporting browsers will already provide useful insights. And most Real User Monitoring tools are able to collect elementtiming, sometimes with limited configuration.
Interaction to Next Paint
Thereâââ‰â¢s further performance risk beyond just load-speed: a CMP script doesnâââ‰â¢t just download: it also executes. That execution can interfere with user interactions. Especially if:
- a CMP script is doing some work while a user tried to interact with other elements on the webpage (input delay as the result of script execution);
- a CMP script is actively doing work when a user tries to interact with the Consent buttons itself (processing duration as the result of CMPs event listeners)
That's right: these are two different moments in time where a CMP can become an issue. And when using RUMvision for my performance audits, I typically see CMPs impacting both. Here's an example of just the execution time:
What we're seeing here is that scripts served from a OneTrust domain is often involved AND comes with high JS execution or processing time on the browser's main thread. This is a big deal as the browser is then not able to do anything else, and the next paint i.e. visual change to the user is pushed back as well.
This is exactly what the INP metric will measure. This LinkedIn post gives a good impression, or reading up via our Interaction to Next Paint (INP) blogpost.
Input delay
Let's first focus on the input delay.
CSS animations
For years, a specific animation behavior has stood out as a primary CPU bottleneck when profiling the consent script's execution. Instead of using the more performant and hardware accelerated CSS transform property, they are using the CSS bottom property.
I even ended up writing about this on LinkedIn in May 2024 after running into this multiple times. Luckily, this can be prevented by injecting custom CSS to switch from OneTrust bottom animation to using the transform property. I once wrote about such fix on LinkedIn related to a different third party script that was showing the same behaviour.
Layout trashing
Because OneTrust's cookie banner uses the top or bottom property to animate itself into view, it needs to know exactly where things are on the page. To figure that out, it calls functions like getBoundingClientRect() or window.innerHeight that force the browser to calculate layout immediately.
This creates what developers call a forced reflow or layout thrashing, which will:
- Make animations feel choppy
- Cause scrolling to lag
- Increase CPU usage (your device works harder)
- Use more energy (worse for battery life)
As OneTrust's x.prototype.animate is privately scoped, site owners cannot intercept nor prevent this from happening. OneTrust should fix this and moving ditching bottom in favor of transform already prevents the need for caling above functions.
Processing duration
When fixing this, your CMP solution (whether being OneTrust or one of many other solutions) is still likely to be one of many INP and thus user frustration causes. This typically happens when a CMP tries to do many tasks in a single batch when site visitors click the accept button.
RUM data then shows the following:
So:
#onetrust-accept-btn-handlerhas a more challenging distribution (only 31% ending up in good responsiveness and 31% poor responsiveness) with mobile P75 processing time being 113 milliseconds#onetrust-reject-all-handlershows slightly healthier distribution (37% ending up in good responsiveness and only 15% poor responsiveness) with mobile P75 processing time being 81 milliseconds
Batched tasks
In short, OneTrust (and many other CMPs) tries to do too much at once within their allowAllEvent function. I tested this on a Windows machine with CPU throttling set to 2x slowdown, resulting in the following flamechart:
closeOptanonAlertBoxfor 11.16 ms (which is the most important part for visitors)hideConsentNoticeV2for 10.44 mswriteGrpParam+writeHstParamfor 2+ mssubstitutePlainTextScriptTagsfor 20.36 msupdateGtmMacrosfor 190.35 msconsentTransactionsfor 6.26 ms
The updateGtmMacros is the biggest bottleneck here. And the more third parties a site is using, the bigger the bottleneck becomes. This is already illustrated by the RUMvision data as the #onetrust-accept-btn-handler button results in extended JS time compared to the reject button.
In reality (and also to be seen in the flame chart), other third parties might have registered event listeners for the whole document or any button as well, claiming their own spot on the main thread when users are interacting with buttons of a CMP. That's why it's important that every third party keeps their time spend within their event listeners to a minimum and only do important work and paint work while deferring every thing else that is not critical from a user experience perspective.
The fix: hide and yield
In other words, t's still the responsibility of the CMP to fully mitigate the impact by deferring dispatching events to GTM. User's won't leave the site within the same moment of accepting third parties, but do want to see the cooke notice disapear as soon as possible when interacting with the CMP buttons.
So it's safe to move dispatch events to the end of the browser's callstack. This can be achieved by yielding in between using the scheduler.yield API. This is supported in Firefox and all Chromium based browsers (Chrome, Edge, Opera, Brave).
The browser is then allowed to first proceed with the visual part: instantly hiding the cookie notice right away with the following in mind: CMPs should limit animations to only using the CSS transform and opacity properties both when showing and hiding the dialog. In the screenshot of the performance panel, we can see how OneTrust also animates the visbility property: a missed performance opportunity.
Custom fix
As the bottom property isn't fixed by OneTrust yet, my expectations for fixing all their performance issues isn't very high. Consequently, I developed a custom workaround for clients using the OneTrust CMP by overriding the #onetrust-accept-btn-handler, since thatâââ‰â¢s where the majority of the issues appear.
Conclusion
Because CMPs are often externally hosted, performance consultants and site-owners alike may feel their hands are tied. But we can still partially mitigate the impact, both from CMPs and from other third-party scripts.
Meanwhile, we will either need to remind third parties of the impact that they have on million of websites or choose for CMP alternatives that already addressed these challenges to make the experiences and lives of our visitors better and reduce frustration to improve conversions.





