How did we make Hotjar work with styled-components?

KP

May 31, 2018

Published by Kris Pinter

Joshua Colley

Bright HR SVG

styled components with hotjar

As part of our new front-end stack we've switched to using Styled Components; we've been very happy with how this has improved our workflow - we'll be blogging about this soon!

Release 3.1 of styled components brought a significant performance improvement by switching how CSS was injected into the page; insertRule replaced appendChild. The only reason we know this is that we have just added Hotjar to our site to help UX get deeper insights into our users behaviour.

Hotjar is one of a suite of tools which listen and record DOM events which can be used to accurately play back a users session; and this is where insertRule becomes a problem - it raises no event Hotjar can listen to so when playing sessions back the styles are completely absent.

Broken Hotjar

We have raised the issue with Hotjar and they accept it's a problem but could offer no solution so we had to go looking for ourselves.

We found this snippet which gave us the inspiration to make our own tweaked version...

Our main concern was performance, we are applying CSS rules twice - how will older browsers or particularly mobile devices cope? To reduce any impact we added a few additions

  1. Proxy insertRule - don't periodically check for changes - listen to the actual calls being made.
  2. Only run the code when Hotjar is recording.
  3. Debounce appendChild so it doesn't trigger lots of updates per second.

Here's the snippet we ended up with - we'd love to hear what you think and if there's any more tweaks we can make to it.

var syncStylesEl = document.createElement("style");
syncStylesEl.type = "text/css";
document.head.insertBefore(syncStylesEl, document.head.children[0]);

var syncHotJarStylesTimeout;
function debouncedSyncHotJarStyles() {
  var later = function () {
    var styleNodes = [].slice.call(
      document.querySelectorAll("head [data-styled-components]")
    );
    if (!styleNodes.length) {
      return;
    }
    var styles = styleNodes
      .map(({ sheet }) =>
        [].slice
          .call(sheet.cssRules)
          .map((rule) => rule.cssText)
          .join(" ")
      )
      .join(" ");
    syncStylesEl.textContent = styles;
  };
  clearTimeout(syncHotJarStylesTimeout);
  syncHotJarStylesTimeout = setTimeout(later, 1000);
}

var originalInsertRule = CSSStyleSheet.prototype.insertRule;
CSSStyleSheet.prototype.insertRule = function (style, index) {
  originalInsertRule.call(this, style, index);
  if (window.hj && window.hj.settings && window.hj.settings.record) {
    debouncedSyncHotJarStyles();
  }
};

Registered Office: Bright HR Limited, The Peninsula, Victoria Place, Manchester, M4 4FB. Registered in England and Wales No: 9283467. Tel: 0844 892 3928. I Copyright © 2024 BrightHR