Making Disqus work with Blazor WebAssembly

Two Disqus plugins
Disqus plugins

I'm creating two Disqus plugins, one for my MVC theme Clarity, and another for a Blazor WebAssembly theme that I'm working on for my blog. Disqus brings comment capability to my site, it displays a comment thread on the bottom of a single blog post, and it shows comment count for all my posts. To achieve these Disqus provides embed and count scripts that I need to put on my pages along with some simple HTML elements.


For the embed script for my MVC theme it requires the following.

  1. Have a div with id disqus_thread on the blog post page, without this element embed.js will throw exception
  2. Before the closing body tag, create a disqus_config object with some configuration info like page URL and ID
  3. Load embed.js itself after disqus_config

For my Blazor WebAssembly theme, it works a bit differently.

  1. Still need the div with id disqus_thread on the page
  2. Don't need disqus_config
  3. Load embed.js if not already loaded
  4. Each time the component that shows the comment thread is displayed, call a DISQUS.reset function

Disqus does not want you to load embed.js each time your component loads as in the case of a multi-page application, if you do it'll throw an warning telling you to use DISQUS.reset instead for a SPA. Here is a sample on how to use reset, and though the sample uses disqus_config, I find it not necessary for it to work in my Blazor app.

The important thing to keep in mind is that DISQUS.reset depends on embed.js to exist first, and embed.js depends on the div element exist on the page

Below is the embed function, it's executed each time a single blog post is shown. The first time the function is called, embed.js is added the DOM, all subsequent calls fall on the DISQUS.reset call. Because a Blazer app is a SPA, the app doesn't refresh after initial load, only portions of the UI need update, thus I have a check to see if embed.js is already loaded.

export function embed(shortname, newIdentifier, newUrl, newTitle, newLanguage) {
    const srcUrl = `https://${shortname}`;
    if (isScriptLoaded(srcUrl)) {
            reload: true,
            config: function () {
       = newIdentifier;
       = newUrl;
       = newTitle;
                this.language = newLanguage;
    let d = document, s = d.createElement('script');
    s.src = srcUrl;
    s.async = true;
    s.type = 'text/javascript';
    (d.head || d.body).appendChild(s);

The count script works in similar fashion, it also needs an HTML element, and also requires a call to a reset function each time the post or posts are displayed.


Disqus plugin for Blazor

For the Disqus.Client plugin, the setup is as follows. I have two JS functions embed and count inside a file disqus.js, and I have an DisqusJsInterop class interfacing them. I also have two components EmbedScript.razor and CountScript.razor, each calling the JS functions through the interop. 

The EmbedScript.razor component has the required div element on it, the component is injected into the BlogPost.razor page component. And the CountScript.razor is injected into BlogPosts.razor page component. The BlogPost.razor displays a single blog post, while the BlogPosts.razor displays a list of blog posts.

At runtime the theme dynamically load the script components. Initially I tried injecting the count script into the layout components MainLayout.razor or BlogPostLayout.razor, thinking this would work the same just like the MVC theme which has the count script on the bottom of the _Layout.cshtml. But in Blazor the dynamically loaded script component placed on the layout component runs only once, unless I navigate to a page that uses a different layout and come back to it then it would run again. Remember the count script needs to call reset every time blog posts are displayed placing it on BlogPosts.razor works well. For the single post, I have an extra call to the count JS function on EmbedScript.razor right after it calls the embed function.