Chat Widget with Perfect Core Web Vitals
Load chat widgets without losing PageSpeed

How to load a chat widget the right way
I have said it over and over again. Some scripts are more important than others. Nobody in the history of the internet has ever been annoyed that a chat widget has not loaded in the first 500ms of page load. That time when the page is still blank.
It would make no sense right, to load a chat widget before the main content of the page has even started loading? No it would make much more sense to first load the most important bits (your logo, your main image, your stylesheets, your fonts, maybe some super important scripts that handle navigation and conversion).
Studies show that only 3 to 10 percent of visitors actually use a chat widget during their session. Deferring the chat script costs you nothing in terms of user experience but gains you everything in terms of page speed.
Unfortunately this is not the way most websites are doing things. On a daily basis I see unimportant scripts (like chat scripts) load with the highest priority immediately at the start of page load.
In this article I will explain how to correctly load a chat script and how this affects important metrics like the Largest Contentful Paint and the Interaction to Next Paint.
Last reviewed by Arjen Karel on February 2026
Table of Contents!
- How to load a chat widget the right way
- Background: how do chat widgets work
- How do chat widgets affect the Core Web Vitals?
- Largest Contentful Paint issues caused by chat widgets
- Interaction to Next Paint (INP) issues caused by chat widgets
- Layout shift (CLS) issues caused by chat widgets
- How to fix Core Web Vitals issues caused by chat scripts
- Fix Cumulative Layout Shift issues caused by chat widgets
Background: how do chat widgets work
A chat widget usually works by loading a small script on your page. That script will load some styles and inject an iframe on your page. An iframe is a small isolated webpage within a webpage. And that iframe will handle everything it needs to chat with your customers.
How do chat widgets affect the Core Web Vitals?
Chat widgets affect the Core Web Vitals in a few ways:
1. They affect the First Contentful Paint and Largest Contentful Paint by competing for early network resources.
2. They affect the Interaction to Next Paint by blocking the main thread and sometimes by slowly updating after interaction.
3. They can cause layout shifts when they do not render correctly on the page.
The impact varies a lot between providers. The heaviest chat widgets (Zendesk, Tawk.to) load 500 to 750 KB of JavaScript. Lighter alternatives like Zoho Desk and Crisp stay under 155 KB. On average, a chat widget adds 300 to 600 ms of main thread blocking time. That is enough to push an otherwise passing INP score into the "needs improvement" range.
Largest Contentful Paint issues caused by chat widgets
A chat widget can affect the Core Web Vitals when it is competing for network resources. JavaScript files are usually queued for download earlier than images. This means, that in the worst case scenario (when the chat script is render blocking) the browser has to wait for the chat script to download and execute before it can continue with anything else. A render-blocking chat widget can double the First Contentful Paint from 1.0 seconds to over 2.3 seconds.
Even when the chat script is deferred it can still impact the paint metrics in a few ways. First let me explain what deferred scripts do. The browser can download deferred scripts in parallel and the browser can continue rendering until the DOMContentLoaded event. After that it will execute the scripts. The problem is that for repeat visitors the LCP element will probably not be loaded at the DOMContentLoaded event but the (cached) chat script will execute causing a delay in LCP metrics.
Interaction to Next Paint (INP) issues caused by chat widgets
A chat widget can and will impact the Interaction to Next Paint in 2 ways. The first way is by blocking the main thread for a short time while the chat widget executes its scripts or checks for updates. This is just how things work. Everything you add to a page will slow down the page a little.
The second way it can cause INP issues is by bad coding (and believe me, there are some poorly coded chat widgets out there). When it comes to chat widgets "more popular" does not mean "better coded". When poor code takes long to update the presentation you will automatically get INP issues. I guess some chat providers need to step up their game. This part is unfortunately out of my control. If you have chosen a poorly coded chat widget there is no way for me to make that code any better.
Layout shift (CLS) issues caused by chat widgets
Sometimes chat widgets cause a layout shift. There are 3 usual suspects that I look for while checking for chat-widget related layout shifts.
- Layout shifts that occur every time on chat load
- Layout shifts that happen on a delayed "chat open"
- Layout shifts that occur when a chat history is loaded (repeat chat visitor)
How to fix Core Web Vitals issues caused by chat scripts
Fortunately it is pretty easy to minimize the impact a chat widget can have on paint metrics (LCP and FCP) and on some parts of the Interaction to Next Paint (INP). In my opening statement I told you that scripts have a time and a place. And for chat scripts that is not "right away and at all costs". I like to load chat scripts after the load event, when the page is not responding to user input and I also like to bypass the preload scanner to avoid network competition.
So how do we do that? We use the load event because when the load event has been fired the LCP element will have been painted on the page (unless you lazy loaded it with JavaScript). We use requestIdleCallback to wait for an idle moment when the browser is not responding to user input. And we use JavaScript to inject the chat script to ensure that the preload scanner does not recognize the script src immediately and trigger an early download (and that is exactly what we want to avoid). This is the same script deferral pattern used for YouTube embeds and Google Maps.
<script>
window.addEventListener('load', function(){
requestIdleCallback(function(){
var s = document.createElement('script');
s.src = 'https://your-chat-widget-url.com/chat.js';
document.body.appendChild(s);
})
})
</script>
Note that requestIdleCallback is not supported in Safari. Use a fallback: const idle = window.requestIdleCallback || ((cb) => setTimeout(cb, 1)); and replace requestIdleCallback with idle in the example above.
With this load event plus requestIdleCallback pattern, the Lighthouse score impact of a chat widget drops from 9 to 16 points to 0 to 1 point.
Alternative: interaction-based loading
Instead of loading the chat widget automatically after the load event, you can wait until the visitor actually interacts with the page. Listen for mousemove, scroll, or touchstart and load the chat script on the first event. This guarantees zero impact on all Core Web Vitals for visitors who never scroll or interact.
<script>
function loadChat() {
var s = document.createElement('script');
s.src = 'https://your-chat-widget-url.com/chat.js';
document.body.appendChild(s);
['mousemove','scroll','touchstart'].forEach(function(e){
document.removeEventListener(e, loadChat);
});
}
['mousemove','scroll','touchstart'].forEach(function(e){
document.addEventListener(e, loadChat, {once: true});
});
</script>
Fix Cumulative Layout Shift issues caused by chat widgets
Chat widgets will usually cause a small layout shift. That does not need to be a huge problem. But sometimes chat widgets just render poorly. Fortunately we can (sort of) fix that too by hiding the poor render until the widget has finished rendering.
To do this we need to read the docs of the chat widget (there are many different chat providers and they all work just a little bit differently). In the docs look for callback functions that get called at different stages of the chat rendering. Since I do not know which chat widget you are using, to illustrate the mechanism we will use the chat.ready() function.
Now with some smart styling we can hide and unhide the chat with the CSS opacity property. First we add some classes to hide the chat widget by default (change the selectors to match your chat widget). Then in the chat.ready() callback we add "showchat" to the body classlist to activate the second CSS line that shows the chat again.
<style>
/*hide chat widget by default*/
.chatwidget{opacity:0}
/*show chat widget after .showchat body class*/
body.showchat .chatwidget {opacity:1}
</style>
<script>
chat.ready(function(){
document.documentElement.classList.add('showchat');
})
</script>
That is it! Good luck speeding up your chat widget. To verify your changes with real visitors, set up Real User Monitoring. Lab scores are useful for debugging, but field data from real users is what Google uses for ranking.
Pinpoint the route, device, and connection that fails.
CoreDash segments every metric by route, device class, browser, and connection type. Real time data. Not the 28 day average Google gives you.
Explore Segmentation
