Using Intercom with wire:navigate
skeemer • September 19, 2023
livewireProblem
While improving a site to use wire:navigate
?, I was seeing errors in the javascript console when navigating that weren't impeding the navigation.
VM842 frame-modern.46eb31cc.js:1 Uncaught TypeError: Cannot read properties of null (reading 'document')
at Object.read (VM842 frame-modern.46eb31cc.js:1:236391)
at ra (VM842 frame-modern.46eb31cc.js:1:400563)
at Session.createOrUpdateUser (VM842 frame-modern.46eb31cc.js:1:402558)
at App.createOrUpdateUser (VM842 frame-modern.46eb31cc.js:1:429677)
at Object.update (VM842 frame-modern.46eb31cc.js:1:434363)
at VM842 frame-modern.46eb31cc.js:1:436689
at <anonymous>:3:108
at <anonymous>:3:606
at swapCurrentPageWithNewHtml (livewire.js?id=28cda9ab:5928:19)
at livewire.js?id=28cda9ab:6092:11
I finally realized that Intercom was disappearing from the page and from the dom, so of course it couldn't be found.
I initially looked into wire:persist
/@persist
. That failed when I found that Intercom didn't support adding the new elements anywhere but the <body>
. That didn't completely kill the solution as I could add it with javascript, except there is also no event created when Intercom is initialized. This could be dealt with by timeouts or some Alpine.js magic, but not really a great solution.
Fortunately, Intercom had to fix this problem for their intercom-rails package. A similar technology, Turbo, had the same issue and was solved by firing events off to Intercom to handle the page changes. The solution provided is not documented at all, but I'm betting that it won't disappear as this is how their official package handles it.
Solution
This basic solution is just adding a couple listeners for wire:navigate
events and fire off the events to Intercom.
document.addEventListener('livewire:navigating', () =>
document.dispatchEvent(new Event('page:before-change')
))
document.addEventListener('livewire:navigated', () =>
document.dispatchEvent(new Event('page:change')
))
This is how I'm using it. I've created the file, config/intercom.php, and I'm putting this in the <head>
because Livewire doesn't reload anything put there.
<head>
...
@if (config('intercom.app_id'))
<script>
// From Intercom docs
const APP_ID = "{{ config('intercom.app_id') }}";
(function(){var w=window;var ic=w.Intercom;if(typeof ic==="function"){ic('reattach_activator');ic('update',w.intercomSettings);}else{var d=document;var i=function(){i.c(arguments);};i.q=[];i.c=function(args){i.q.push(args);};w.Intercom=i;var l=function(){var s=d.createElement('script');s.type='text/javascript';s.async=true;s.src='https://widget.intercom.io/widget/' + APP_ID;var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s, x);};if(document.readyState==='complete'){l();}else if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}})();
// Fire events for changing pages
document.addEventListener('livewire:navigating', () => document.dispatchEvent(new Event('page:before-change')))
document.addEventListener('livewire:navigated', () => document.dispatchEvent(new Event('page:change')))
</script>
@endif
</head>
<?php
return [
'app_id' => env('INTERCOM_APP_ID', ''),
'id_secret' => env('INTERCOM_ID_SECRET', ''),
];