loading...
Cover image for How to embed a Changelog Podcast Player into a React app?

How to embed a Changelog Podcast Player into a React app?

xxczaki profile image Antoni Kepinski ・3 min read

Introduction

I recently had a chance to do a small redesign of my personal site. One of the goals, besides making everything look cleaner, was to embed Changelog's Podcast Player, so that it would play 85th episode of JS Party, in which I talked about one of my projects.

It seems pretty straight forward:

<audio data-theme="night" data-src="https://changelog.com/jsparty/85/embed" src="https://cdn.changelog.com/uploads/jsparty/85/js-party-85.mp3" preload="none" class="changelog-episode" controls></audio><p><a href="https://changelog.com/jsparty/85">JS Party 85: Building PizzaQL at the age of 16</a> – Listen on <a href="https://changelog.com/">Changelog.com</a></p><script async src="//cdn.changelog.com/embed.js"></script>

And works just fine on a static HTML site. However, in my case, I was dealing with a React application, powered by Next.js with Static Site Generation (SSG). I also focused much on performance and accessibility.

That's exactly why I decided to make this tutorial!

Tutorial

First of all, we need to embed an audio tag into our app. It's fairly easy:

<audio
    controls
    data-theme="night"
    data-src="https://changelog.com/jsparty/85/embed"
    src="https://cdn.changelog.com/uploads/jsparty/85/js-party-85.mp3"
    preload="none"
    className="changelog-episode"
/>

Make sure to replace data-src and src attributes so that they link to an episode, which you want to embed. It does not need to be the JS Party podcast.


We now need to load a script, which will add an iframe with the player itself. Normally, we would obtain it from https://cdn.changelog.com/embed.js, but instead, we are going to self-host it. Here are the reasons why:

  • Self-hosting assets (like fonts or scripts) is significantly faster;
  • The default Changelog's script does not add a title attribute to the created iframe - this will lower our site accessibility score on tools like Lighthouse;
  • The default Changelog's script sometimes throws Uncaught TypeError: Cannot read property 'postMessage' of null..*

*This does not break the functionality of the player and therefore we can easily fix it using a try...catch statement.

In order to edit the embed script, we first need to unminify the original one from Changelog's CDN. We can then modify it like so:

!(function (e) {
    function t(t) {
        var r = t.getAttribute("data-src"),
            i = t.getAttribute("data-theme") || "night",
            n = e.createElement("iframe");
        n.setAttribute("src", r + "?theme=" + i + "&referrer=" + e.location.href),
            n.setAttribute("width", "100%"),
            n.setAttribute("height", "220"),
            n.setAttribute("scrolling", "no"),
            n.setAttribute("frameborder", "no"),
+           n.setAttribute("title", "Podcast"),
            t.parentNode.replaceChild(n, t),
            (this.id = +new Date()),
            (this.src = n.src),
            (this.iframe = n);
    }
    var r = "https://changelog.com",
        i = e.getElementsByClassName("changelog-episode"),
        n = [],
        a = function (e, t) {
-            (t.context = "player.js"), (t.version = "0.0.11"), (t.listener = e.id), e.iframe.contentWindow.postMessage(JSON.stringify(t), r);
+            try {
+                (t.context = "player.js"), (t.version = +"0.0.11"), (t.listener = e.id), +e.iframe.contentWindow.postMessage(JSON.stringify(t), r);
+            } catch {}
        };
    window.addEventListener("message", function (e) {
        if (e.origin !== r) return !1;
        var t = JSON.parse(e.data);
        if ("player.js" !== t.context) return !1;
        if ("ready" === t.event) for (var i = n.length - 1; i >= 0; i--) n[i].src === t.value.src && a(n[i], { method: "addEventListener", value: "play" });
        if ("play" === t.event) for (i = n.length - 1; i >= 0; i--) n[i].id !== t.listener && a(n[i], { method: "pause" });
    });
    for (var s = i.length - 1; s > -1; s--) n.push(new t(i[s]));
})(document);


We are going to load the script in a useEffect hook so that it won't run on the server (where a browser API, like document and window, isn't available):

useEffect(() => {
    const script = document.createElement('script');
    script.src = '/embed.js'; // Since I'm using Next.js I can place the file in the `public` folder and refer to it like so.
    script.async = true; // Make sure the script isn't render-blocking.
    script.defer = true;
    document.body.append(script);
}, []);

That's it! You now have a beautiful player on your site.

Credits

The approach of using the useEffect hook was taken from React NodeGui website source code.


Thank you for reading my post! I hope you will find it useful ;)

Posted on by:

xxczaki profile

Antoni Kepinski

@xxczaki

17 years old developer ⚡️ Into Node.js & Rust 🛠

Discussion

markdown guide