Drift Chatbot – Optimize for Page Speed

Update July 9th 2020: I’ve created a WP plugin called Chatbot Lazy Loader to handle the optimization of your chatbot. It even hit #3 on Product Hunt.

Chatbot Lazy Loader - Eliminate page speed issues caused by chatbots on WordPress | Product Hunt Embed

When we’re considering JS dependent functionality, we need to weight its benefits against its costs. Chat bots are powerful support and lead gen tools, but like any JavaScript resource it adds weight to your web page, slows TTI, and delays other resources from loading.

We should also consider smarter ways of including our JavaScript. The easiest optimization to make is to only include JavaScript on the pages that require it. That’s simple enough, right? But with Chatbots, it’s less of a page specific issue and a timed intent issue.

So if you want to keep your Drift chat bot, but avoid the hit to your page speed, here’s how to optimize the way you include Drift’s JavaScript.

Put your visitors first

Think of the way visitors are using your website. It’s a safe assumption to make that most visitors are not using a chatbot every time they view a page on your website. They are most likely going to hit an entry point, move through a flow, and at some point they may have a question that prompts them to use a chatbot.

So let’s not make them download that JavaScript when they don’t need it. Instead we can allow visitors to download that JavaScript on-demand, that is – only when they want to use the chatbot.

This is exactly what we did at Biz Rocket when we first added Drift back in January. Drift later on released a tutorial on how to do something similar to what we did.

Drift’s standard install instructions

When you create a Drift account, you’re given this JavaScript snippet with instructions to add it to the <head> of your website:

<!-- Start of Async Drift Code -->
<script>
"use strict";

!function() {
  var t = window.driftt = window.drift = window.driftt || [];
  if (!t.init) {
    if (t.invoked) return void (window.console && console.error && console.error("Drift snippet included twice."));
    t.invoked = !0, t.methods = [ "identify", "config", "track", "reset", "debug", "show", "ping", "page", "hide", "off", "on" ], 
    t.factory = function(e) {
      return function() {
        var n = Array.prototype.slice.call(arguments);
        return n.unshift(e), t.push(n), t;
      };
    }, t.methods.forEach(function(e) {
      t[e] = t.factory(e);
    }), t.load = function(t) {
      var e = 3e5, n = Math.ceil(new Date() / e) * e, o = document.createElement("script");
      o.type = "text/javascript", o.async = !0, o.crossorigin = "anonymous", o.src = "https://js.driftt.com/include/" + n + "/" + t + ".js";
      var i = document.getElementsByTagName("script")[0];
      i.parentNode.insertBefore(o, i);
    };
  }
}();
drift.SNIPPET_VERSION = '0.3.1';
drift.load('hrypg4pnzn33');
</script>
<!-- End of Async Drift Code -->

This is the exact code that’s running at: https://www.bizrocket.net/drift-test-head. The code is added to the <head> of the document. That means that it’s render blocking and it will affect the page’s TTI.

I ran this test trough Lighthouse and this is what I got:

Not too impressive, especially since the the page is very thin and the rest of the site scores in the upper 90s/100.

So let’s see what we can do to improve things.

Optimizing Drift for page speed

Instead of loading that JavaScript on every page, let’s only add the JavaScript when someone clicks a “Chat with us” button.

Step 1 – Add Drift’s JavaScript as a function

function LoadDriftWidget(el) {
// Paste all of the JavaScript in here
}

Then, somewhere on your page add a button that, when clicked, calls the LoadDriftWidget() function.

<button id="a-unique-id" onclick="LoadDriftWidget(this.id)">Let’s Chat</button>

Clicking that button will download the Drift JavaScript on-demand and then launch the chat widget. And this is great. It’s much better than what we had before.

But we can do better.

With that implementation, what happens when someone clicks the button?

  1. You click the button
  2. Wait a second or so
  3. Chat bot appears

That’s fine if the chat bot appears very quickly, but if there’s even just a slight delay it may appear that the button is broken. What we want to do is give the visitor feedback so that they know that clicking the button worked and that the chat bot is loading.

Step 2 – Provide feedback

The HTML button should have a unique ID. That’s easy enough to automate in JavaScript or PHP. And we’ll use that ID when the visitor clicks the button:

// Code to run when a Drift button is clicked
function LoadDriftWidget(el) {
    // Get the initial button text and store it for later
    var text = document.getElementById(el).innerText;

    // Change the button text to a loading indicator
    document.getElementById(el).innerText = "loading...";

    // ... the rest of the Drift JavaScript

    // Once Drift is loaded and ready
    drift.on('ready', function(api, payload) {
       // Reset the button text to its initial state
      document.getElementById(el).innerText = text;
    }

}

Now when someone clicks the button, the button text changes to “loading…” and as soon as the chatbot appears it changes back to its original state.

Again, much better. But I noticed one small hick up in this implementation. If someone were to click the chatbot a second time, after the script has already loaded, nothing happens. The Drift JavaScript has already been loaded, so there’s nothing we should expect this code to do.

Step 3 – Launch the Drift sidebar

Drift has example JavaScript to interact with the chat widget. One of the features is the ability to programmatically open and close the sidebar widget.

So here’s how I changed the code to open the sidebar widget if the chat button was clicked two or more times:

// Initiate the counter
var i = 0;

// Code to run when a Drift button is clicked
function LoadDriftWidget(el) {
    // Increment the counter
    i++;
  
    // ... rest of the example JavaScript

    // Once Drift is loaded and ready
    drift.on('ready', function(api, payload) {
    
        // Reset the button text to its initial state
        document.getElementById(el).innerText = text;

        // If a Drift button is clicked more than once, open the chat sidebar
        // If this is not the first time the button was clicked, open the sidebar
        if (i >= 2) {
            api.sidebar.open();
        }
    });
});

This is the exact code that I have running at Biz Rocket and I created a separate page to compare against our first Lighthouse test: https://www.bizrocket.net/drift-test-optimized/

Obviously that’s much better considering the fact that this was 3 minutes worth of work.

Here’s the final code in full:

"use strict";

// Initiate the counter
var i = 0;

// Code to run when a Drift button is clicked
function LoadDriftWidget(el) {
  // Increment the counter
  i++;
  
  // Get the intial button text
  var text = document.getElementById(el).innerText;

  // Change the button text to a loading indicator
  document.getElementById(el).innerText = "loading...";
  

  // Start Drift Widget Code
  var t = window.driftt = window.drift = window.driftt || [];
  if (!t.init) {
    if (t.invoked) {
      api.sidebar.open()
      return void (window.console && console.error && console.error("Drift snippet included twice."));
    }
    t.invoked = !0, t.methods = [ "identify", "config", "track", "reset", "debug", "show", "ping", "page", "hide", "off", "on" ], 
    t.factory = function(e) {
      return function() {
        var n = Array.prototype.slice.call(arguments);
        return n.unshift(e), t.push(n), t;
      };
    }, t.methods.forEach(function(e) {
      t[e] = t.factory(e);
    }), t.load = function(t) {
      var e = 3e5, n = Math.ceil(new Date() / e) * e, o = document.createElement("script");
      o.type = "text/javascript", o.async = !0, o.crossorigin = "anonymous", o.src = "https://js.driftt.com/include/" + n + "/" + t + ".js";
      var i = document.getElementsByTagName("script")[0];
      i.parentNode.insertBefore(o, i);
    };
  }
  
  drift.SNIPPET_VERSION = '0.3.1';
  // Add your own Drift ID here
  drift.load('XXXXXXXXXX');

  // Once Drift is loaded and ready
  drift.on('ready', function(api, payload) {
    
    // Reset the button text to its initial state
    document.getElementById(el).innerText = text;

    // If a Drift button is clicked more than once, open the chat sidebar
    if (i >= 2) {
      api.sidebar.open();
    }
  });
};