Skip to main content

A Simple Fix for Dynamicly Added External Widgets in Turbo-Enabled Sites

While working on Humadroid, I recently stumbled upon an interesting challenge with our chat widget. It’s a small discovery, but one that might save other developers some headaches.

The Problem

If you’re using Turbo (formerly Turbolinks) and external widgets like Chatwoot, you might notice they disappear or reload whenever you navigate between pages. This happens because these widgets usually attach themselves to the end of your DOM, and Turbo replaces all that content during page transitions.

The common solution floating around (including in Chatwoot’s community - Github - bug: Turbo + Stimulus issues.) is to reinitialize the widget on every turbo:load event. While this works, it’s not ideal - it makes page loads slower and creates unnecessary network requests. Not great for user experience.

A Better Solution

After some tinkering, I found a simpler approach. Instead of reinitializing the widget, we can preserve it across page loads. Here’s how:

  1. First, add a permanent holder div in your layout:
<div id="permanent-holder" data-turbo-permanent="true"></div>
  1. Then, move the widget elements into this holder once they’re initialized:
(function(d,t) {
  var BASE_URL="https://base.url";
  window.addEventListener("chatwoot:ready", function () {
    if(!window.chatwootMoved){
      var elements = ['cw-bubble-holder', 'cw-widget-holder',
                     'chat_script', 'cw-widget-styles'];
      const parent = document.getElementById("permanent-holder")
      elements.forEach((elementId) => {
        var el = document.getElementById(elementId)
        if(el){
          parent.appendChild(el)
        }
      });
      window.chatwootMoved = true
    }
    window.$chatwoot.setUser('<%= current_user.id %>', {
      name: "<%= current_user.to_s %>"
    });
  })
  // Rest of initialization code...
})(document,"script");

That’s it! The data-turbo-permanent attribute tells Turbo to keep these elements around during page transitions. It’s a small change, but it made Humadroid’s navigation noticeably smoother.

This approach should work for any external widget that dynamically adds elements to your page - not just Chatwoot. Sometimes the simplest solutions are the best ones.