Lately, I’ve been obsessed with optimizing performance through lazy-loading. Recently, I’ve written on how to lazy-load Google Maps and on how to lazy-load responsive Google Adsense. Now it’s time for Disqus, a service for embedding comments on your website. It’s a great service. It eliminates the headache of developing your own local commenting system, dealing with spam, etc. Recently, I’ve been working on implementing the widget in one of my projects.
The Problem
Layout-wise, comments usually play a secondary role. In many cases, comments are never seen at all by the visitors, because the don’t scroll down to where they are. But guess what? By default, they get loaded on the website no matter what. The wasted bandwidth is a problem.
Take a look at the technical implementation officially recommended by Disqus:
<div id="disqus_thread"></div>
<script>
(function() {
var d = document, s = d.createElement('script');
s.src = '//username.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
Here’s what they say: “Place the following code where you’d like Disqus to load”. Say you’re a good developer and you usually insert all of the <script src="..."></script>
fragments right before the closing tag </body>
. Then, one fine day, you decided to implement Disqus comments on your website and placed the above code somewhere in the middle of the document where the commenting section was meant to be.
What happens? The very first JavaScript file to start downloading is username.disqus.com/embed.js
. That does not necessarily mean it will be downloaded first, but it’s the first one in the line of JavaScript files that gets the browser’s attention. Shouldn’t the first be reserved for the main JavaScript file of your website? There are many things (like “sleeping” <button>
‘s, etc.) that could go wrong when your main JavaScript file is late to load, especially if you were not following the principles of graceful degradation or progressive enhancement back then when you developed that website.
This also interferes with other external resources on your website, like images and CSS files. Imagine yourself using a smartphone under 2G network conditions and waiting for the comments widget to load because you came for a kitten photo.
I did a test. Turns out the Disqus widget with zero comments weighs 2.49 MB! A bunch of networks requests for JavaScript, CSS, image, and font files that in many cases unreasonably slow down serving the other, perhaps critical parts or functions of your website.

The Solution: Tiny JavaScript Plugin
In order to be able to lazy-load Disqus, I developed a tiny JavaScript plugin which does the job. No matter where the comments zone is, above or below the viewport, it won’t get loaded if there isn’t any reason to:

The plugin itself is a tiny piece of JavaScript. I made two versions of it: vanilla and jQuery. I called it disqusLoader. You can grab the files here:
- disqusloader.js; no dependencies (IE 9+).
779 bytes when minified and gzipped. - jquery.disqusloader.js; jQuery dependency.
569 bytes when minified and gzipped.
Here’s you you set it up. First, you need to insert an element into HTML where you want the comments section to be:
<div class="disqus"></div>
Then, initialize the plugin like this:
// vanilla
disqusLoader( '.disqus', { scriptUrl: '//username.disqus.com/embed.js' });
// jQuery
$.disqusLoader( '.disqus', { scriptUrl: '//username.disqus.com/embed.js' });
“That’s great, but what about Disqus-specific config,” you may ask. Sure, there’s one more argument available which accepts a Disqus-native value. There are also a few more plugin-related options:
var options =
{
scriptUrl: '//username.disqus.com/embed.js',
/*
@type: string (url)
@default: none
@required
URL of Disqus' executive JS file. The value is memorized on the first function call
and ignored otherwise because Disqus allows only one instance per page at the time.
*/
laziness: 1,
/*
@type: int (>=0)
@default: 1
Sets the laziness of loading the widget: (viewport height) * laziness . For example:
0 - widget load starts when at the least a tiny part of it gets in the viewport;
1 - widget load starts when the distance between the widget zone and the viewport is no more than the height of the viewport;
2 - 2x viewports, etc.
*/
throttle: 250,
/*
@type: int (milliseconds)
@default: 250
Defines how often the plugin should make calculations during the
processes such as resize of a browser's window or viewport scroll.
250 = 4 times in a second.
*/
/*
@type: function
@default: none
Disqus-native options. Check Disqus' manual for more information.
*/
disqusConfig: function()
{
this.page.title = 'Page Title';
this.page.url = 'http://url.to/your-website';
this.page.identifier = 'unique-identifier';
}
};
// vanilla
disqusLoader( '.disqus', options );
// jQuery
$.disqusLoader( '.disqus', options );
Check it out for yourself:
You can also contribute or follow the project on GitHub.
Disqus Callbacks
Callbacks are great because you can react to user’s actions. There’s only one kind of callback officially documented by Disqus. Looking at the source code of `embed.js` file you can see more pre-defined types of callbacks:

However, looks like only two of them are enabled: onNewComment
and onReady
. Which is enough for a tiny but noticeable improvement: a loading indicator.
Complementary Loading Indication
Loading the Disqus widget usually consists of two parts:
- Loading the `embed.js` file
- Loading the inner assets and performing other types of network requests
Disqus itself takes care of the 2nd step which they indicate with the animated image. But what about the 1st step? There are many reasons why loading external JavaScript files could take tens of seconds. The catch here is that no matter what the network conditions are, users will still be informed there’s a commenting feature available on your site. User experience is in the details!

The technical approach is simple: a new HTML element and JavaScript callback function which helps to hide the element:
<div class="disqus-placeholder">Loading comments...</div>
<div class="disqus"></div>
// vanilla
disqusConfig: function()
{
this.callbacks.onReady = [function()
{
var el = document.querySelector( '.disqus-placeholder' );
if( el.classList )
el.classList.add( 'is-hidden' ); // IE 10+
else
el.className += ' ' + 'is-hidden'; // IE 8-9
}];
}
// jQuery
disqusConfig: function()
{
this.callbacks.onReady = [function()
{
$( '.disqus-placeholder' ).addClass( 'is-hidden' );
}];
}
.disqus-placeholder.is-hidden { display: none; }
You can see this in action on the demo page. But first, clean the browser’s cache and throttle the network speed.
Multiple Instances Or Sites At Once?
While working on the technique, I discovered a couple of important limitations that exist today…
It’s Impossible To Have Multiple Script Sources On a Single Page
Once the script file (e.g. //username.disqus.com/embed.js
) loads, the global variable window.DISQUS
is created, but only if it wasn’t set previously (which is a bad sign, but let’s dig deeper). So, I’ve done a test. I initialized the widget from the source script A. Then freed up some space for the future variable window.DISQUS = undefined
and initialized the widget of the source B. However, the result was a mess: callback functions were fired multiple times, the comments got duplicated, etc. Obviously, the current codebase of Disqus is not designed to support multiple variables and to operate individually with each widget instances.
It’s Impossible To Have Multiple Widgets On a Single Page At Once
There’s a public JavaScript method function reset()
available within the DISUQS
object. If you’ve had any technical experience with Disqus, you may know that the widget is inserted into an element which has disqus_thread
value for the id
. I’ve done a test with two elements: loaded the widget within the first element, removed the ID attribute, and appended it to the second element. Finally, I called the reset function, expecting the second instance just to appear next to the first one. However, calling the function to load a new instance also destroys any previously initialized widgets. Unfortunately, today Disqus is designed only for a single instance at the given time.
It’s Possible To Reload Widget In Real-Time
There’s one good thing! Even though it is not possible to have multiple widget instances at once, you can still destroy the old ones and load in new ones. Let’s turn this theory into practice with probably the most typical situation: tabs. All you need to do is call the plugin each time the new tab is activated:
<div class="tabcontent" data-disqus-id="venus" data-disqus-title="Venus"></div>
<div class="tabcontent" data-disqus-id="earth" data-disqus-title="Earth"></div>
<div class="tabcontent" data-disqus-id="mars" data-disqus-title="Mars"></div>
// call this function every time a tab is clicked:
var initDisqus = function( content )
{
disqusLoader( content,
{
scriptUrl: '//username.disqus.com/embed.js',
disqusConfig: function()
{
this.page.identifier = content.getAttribute( 'data-disqus-id' );
this.page.title = content.getAttribute( 'data-disqus-title' );
}
});
}
You can see this in action or view the full code on the demo page.
Closing Thoughts
This post is not about the flaws in Disqus. It’s about the mistakes we developers make. Our world is full of tools and it is up to us how we use them. Even though these tools solve particular problems, they usually bring some others along if we don’t take the appropriate care when implementing them. Every choice to take the easiest way turns into lost users, decreased conversions, and increased bouncing rates. You can already lazy-load Disqus, Google Adsense, Google Maps, social media buttons, you can also develop and share custom techniques. Be responsible!
Pretty darn Amazing!!! I think a client has something like this in their Disqus. TBH I never even thought of it as a big issue before as I also did not test the load from disqus. We actually recommend only loading comments if someone chooses to, so there is lower cost to self-hosted or external-service comments, and intent has to be there, but this is a really compelling alternative that has a lot of the same benefits and helps users that might say or share something wonderful but don’t click through for whatever reason.
This is a great idea that somehow never even crossed my mind. Similar to what Lewis Cowles said, I never thought about this issue until I saw this post. Once the topic was brought up I started testing the load and in many cases it is larger than the entire page’s resources combined.
Also the note about people putting the script where they want it to display comments rather than prioritizing in the footer . . . yep, that’s exactly what I did.
Thank you very much for this project and for making this post. I lazy-load many things but for some reason, comments didn’t even cross my mind to look into and now I have something to help me do it. Thank you!
I’m not seeing the same as you. Disqus does not take up 2.49 MB on my site (more like 250kb).
Also, while some parts of Disqus are loaded when the page loads, the comments themselves are already lazy loaded only when they are shown in the view port. At least that’s my experience on Chrome anyway…
quarter of a MB… Just think about that (regardless of the size originally quoted). 250kb on a slow connection or 3G or spotty 1-bar WiFi is not the best experience. Only loading when needed would very definitely improve their experience wouldn’t it?
Totally inline with TrueTheWeb.
I was actually amazed about the quoted 2.49MB in this article and run to my site to check.
Apparently this is false advertising. The article should state the correct burden on the site and allow the readers to judge whether this is acceptable or not.
This is just unacceptable.
For typical user Disqus script loads about 814.32KB worth of data (comment block without any comments). If you are logged in to Disqus system as admin, there’s more than 2MBs of data to download.
Could have done the magic ?
Of course, but better add that lightweight dependency… Hahaha
Its so cool
You are checking the viewport.
But the second most important thing is URL:
e.g. if you sending a link to the comment it typically looks like
URL#comment-2924759399
.In this case, as only Disqus is loaded it scrolls a page to the comment.
This functionality is missed in your plugin, to check if the URL points to a comment.
window.location.hash is your friend. Perhaps this plugin needs some contribution. I’m quite sure you can find the viewport test and upon page load completion if there is a hash matching regex
/[#]comment[-]\d+/
then trigger the plugin. It also needs a callback that scrolls to the comment once loaded and some state, so you are only detecting comment once, but it’s hardly the end of the world after the author has come this far for you to do a little work and thought…Just added the lazy loading script to our disqus comments (Example Here). Great job, overall does what it says. A couple of observations though on for others when adding the script:
In your instructions you mentioned to create a DIV “. You should add that the script then dynamically injects `id=disqus_thread` into that DIV. I was adding my own custom ID to the DIV, as I wanted it to act as a wrapper to the Disqus container and dynamically show/hide it, though the console kept returning `document.getElementByID(…)` is null. Took me a while to realize what was going on.
If the style of the `.disqus` DIV is initially set to `display:none`, the script will NOT lazy load the comments, and simply load it as soon as the page loads. Again, was trying to initially hide the comments wrapper on the page, though had to work around that due to this behavior.
It seems sometimes the comments get loaded before the user is anywhere near the comments container on the page. On this page for example, scrolling half way lazy loads the comments.
Thanks for the good work!
Hey, Disqus here.
First of all, thank you for using Disqus and spending time on improving the developer experience of it. When I read your article, I’ve realized there are some points that may use clarification from a first-level source so here they are :)
It mostly isn’t because all of the assets and scripts Disqus uses are very heavily cached so you don’t really waste much bandwidth if any at all.
Completely misleading. First of all, the browser appends the script at the end of its priority list since script elements created from scripts are loaded asynchronously. That’s why we used the inline script approach. Nowadays with evergreen browsers, we can simply add the
async
attribute to have the same benefits on top of the browser’s forward scanning so it detects the file even earlier (but still puts it in a low priority queue because of the async attribute). You can read more at this excellent article by Ilya Grigorik: https://www.igvita.com/2014/05/20/script-injected-async-scripts-considered-harmful/This is again, completely false. What you are seeing is probably the preloading of our extended community features which are only preloaded after everything on the page (including the whole Disqus commenting widget) has loaded. This also happens only on desktop browsers both to save bandwidth on mobile devices and simply because we cannot really show a sidebar on mobile devices. The core size is about 220kb for a page full of comments. This includes embed.js, the loading spinner and much of everything else really. Also, this is very heavily cached so you pay this price only once. Any consequent loads even on different pages are a lot cheaper since you have almost everything in your cache.
I also want to add that to optimize things even further, we actually delay loading all images (including user avatars), rich media content like YouTube embeds etc. unless you scroll further down so that we think you will likely to see them. We do a similar thing for the code highlighting feature: unless we know there’s code to highlight in a comment, we simply do not load that library so you don’t pay the cost.
You can always put a string in your
#disqus_thread
div saying something like “Disqus comments” which will be replaced with the actual content automatically. We actually used to have a text saying “Comments powered by Disqus” where the Disqus part was a link but then people thought we were trying to get backlinks for Google so we removed it (and no, we were not trying to get backlinks for Google ranking, we just missed therel=nofollow
attribute).Not really. Setting
window.DISQUS
tonull
does not make the existing and already executed code go away. It just makes it very hard or impossible to reach. That’s why we intentionally skip redefining things when we realize you already loaded Disqus on a page. This saves your page, not us :)There’s really no need to remove the id from the previous element and then give it to a new one. There is a not-officially-supported (but works-now-and-has-worked-for-quite-a-while)
disqus_container_id
which you can set the id of the container you want Disqus to load in. Again, not officially supported so if it stops working one day, you are warned :)This is intentional to prevent leaks since
DISQUS.reset
, as the name suggests, was meant to reset the widget not spin a new one. There are a few parts in the code that assume it is going to be the only Disqus widget active on the page but the overall architecture actually allows having many widgets. It is mostly the usability bits that we were concerned so we closed this path. Interested in hearing out why you want to load multiple widgets at the same time on a page tough.Yes and this was already publicly documented as you have suggested where we tell people about
DISQUS.reset
. The danger in this is, it is very easy to break your threads by using multiple identifiers for the same URL as you did. Your comment threads will be split and hard to recover with that. See this for more information: https://help.disqus.com/customer/en/portal/articles/2158629-use-configuration-variables-to-avoid-split-threads-and-missing-commentsCompanies like Disqus exists so you worry about one less thing. We are a dedicated and very experienced team of engineers and other people who care a lot about loading performance, playing nice with the pages we are embedded, having the smallest possible footprint along with great caching and a great user experience. We are not perfect, yes, but at least we are a dedicated team to make this part of your page and experience awesome so you don’t have to worry about it. You can bet that we are responsible and will stay that way for the foreseeable future ;)