How to Use Meteor.js Routing With Disqus

On most sites, using Disqus as the commenting system is as simple as copying the Universal Code (also known as embed.js) into a web page. In single-page JavaScript applications, however, much of the rendering is on the client-side. This means that embed.js might lead to stale Disqus threads or function incorrectly as pages are re-rendered.

This article explains how to integrate Disqus with Meteor.js (version 0.6.4.1 at the time of writing). If you want one Disqus thread for each route, copying the Universal Code won’t ensure that Disqus loads the correct thread for each page. Instead, the comments will either fail to load or retrieve a stale thread.

When embed.js is loaded multiple times (when pages rerender and run the script multiple times), the following error will appear in the console:

DISQUS assertion failed: Unsafe attempt to redefine existing module <name>

Note: I’m using Meteor Router to create URL paths in my application, although this method should work with any routing system.

Disqus Single Sign-on

As a bonus, I’ve added the optional code snippet in Deps.autorun() for using Disqus Single Sign-on with Meteor. If you want to see the server-side code in JavaScript, take a look at the examples that I added to the Disqus API Recipes.

Annotated Code

The code is written in CoffeeScript. If you want to take a look at the JavaScript version, you can use the CoffeeScript translator. Meteor automatically compiles .coffee files to .js.

With the code below, all you have to do is add the following to your Meteor Handlebars template to include Disqus comments:

1
 {{> disqus}} 

Easy, right?

Meteor Disqus template manager (disqus.coffee) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Template.disqus.rendered = ->
  ###
  # We don't want to load Disqus until the first time the template is
  # rendered, since it requires the #disqus_thread div
  # Triggers Deps.autorun (below)
  ###
  Session.set("loadDisqus", true)

  ###
  # OPTIONAL: Only include the part below if you're using
  # Disqus single-sign on
  # Generate the disqusSignon variable appropriately from your server
  ###
  disqusSignon = Session.get("disqusSignon")
  if Meteor.user() and disqusSignon
    window.disqus_config = ->
      this.page.remote_auth_s3 = "#{disqusSignon.auth}"
      this.page.api_key = "#{disqusSignon.pubKey}"
      # ... other Disqus configs 

    ###
    # Whenever the template is rendered, trigger a Disqus reset.
    # This will find the correct thread for the current page URL.
    # See http://help.disqus.com/customer/portal/articles/472107-using-disqus-on-ajax-sites
    ###
    DISQUS?.reset(
       reload: true
       config: ->
    )


Deps.autorun(->
  # Load the Disqus embed.js the first time the `disqus` template is rendered
  # but never more than once
  if Session.get("loadDisqus") && !window.DISQUS
    # Below is the Disqus Universal Code
    # (in Coffeescript, backticks escape Javascript code)
    `
    /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
    var disqus_shortname = 'YOUR_SHORTNAME'; // required: replace example with your forum shortname

    /* * * DON'T EDIT BELOW THIS LINE * * */
    (function() {
     var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
     dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
     (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
     })();
     `
)
Meteor Disqus template (disqus.html) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template name="disqus">
{{#isolate}}
<div id="my-disqus">
  <!-- Begin Disqus universal code -->
  <div id="disqus_thread"></div>

  <noscript>Please enable JavaScript to view the
    <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a>
  </noscript>

  <a href="http://disqus.com" class="dsq-brlink">
    comments powered by <span class="logo-disqus">Disqus</span>
  </a>
  <!-- End Disqus universal code -->
</div>
{{/isolate}}
</template>

Disqus in Other Single-page JavaScript Frameworks

If you’re using another single-page JavaScript framework like Ember.js, Angular.js, etc., here’s a general strategy for using Disqus:

  1. Wait until the first time a Disqus thread needs to be loaded.
    When div #disqus_thread has been rendered, load embed.js into the page.
  2. Set a global flag to ensure that embed.js is not loaded again.
  3. Each time a new Disqus thread is needed, trigger a DISQUS.reset(), including the first time embed.js is loaded. Again, make sure that div #disqus_thread is rendered in the DOM, or there may be an error about appending an element to null.

Comments