Load a chat widget with perfect Core Web Vitals

Load a chat widget that does not interfere with PageSpeed and the Core Web Vitals

Arjen Karel Core Web Vitals Consultant
Arjen Karel

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 even been annoyed that a chat widget has not loaded in the first 500ms of pageload. That time when the page is still blanc.

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)

Unfortunately this is not the way most websites are doing things. On a daily basisi I see unimportant scripts (like chat script) 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.

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 idrame 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 if a few ways:

1. They affect the First Contentful Paint & 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

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 usually are queued for download earlier then images. This means, that in the worst case scenario (when the chat script is render blocking) the browser whas to wait for the chat script to download and execute before it can continue with anything else.

Even when the chat script is deferred it can still impact the paint metrics is 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 it's scripts or checks for updates. This is just how things work. Every thing 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'. Then 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 & 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 ad 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 and trigger an early download (and that is exactly what we want to avoid!)

    /* requestIdleCallback fallback for older browsers  */
    var safeRequestIdleCallback = window.requestIdleCallback || function (cb) {
        return setTimeout(function () {
        }, 1);
    /* execute after the load event */
    window.addEventListener('load', function () {
        /* when browser is idle */
        safeRequestIdleCallback(function () {
            /* inject script, bypass preload scanner */
            var script = document.createElement('script');
            script.src = 'your-chat-script.js';

    Fix Cumulative Layout Shifts 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 don't know which chat widget you are using to illustrate the mechanism for now 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 descriptors 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.
    /*hide chat widget by default*/
    /*show chat widget after .showchat body class*/
    body.showchat .chatwidget {opacity:1}
    }) <script>

    That's it! Good luck speeding up your chat widget

    I help teams pass the Core Web Vitals:

    lighthouse 100 score

    A slow website is likely to miss out on conversions and revenue. Nearly half of internet searchers don't wait three seconds for a page to load before going to another site. Ask yourself: "Is my site fast enough to convert visitors into customers?"

    Load a chat widget with perfect Core Web VitalsCore Web Vitals Load a chat widget with perfect Core Web Vitals