eleventy-import-disqus
This is an intermediate step to migrate off of Disqus altogether but keep static copies of existing comment content.
Live demo (scroll down)
Blog post
Read more at theBenefits:
- Way speedier than Disqus and no third party JS.
- Comments available without JS.
- Properly maintains threading.
- Uses gravatar for avatar images.
- Works with any existing links to Disqus comments in the wild (and there isn’t an annoying delay while the Disqus loads and jumps to the comment).
- Full control over markup and style of comments (samples provided below).
Drawbacks:
You’ll need a new mechanism to add comments.
Get your Disqus XML
- Clone this repository locally.
- Export your Disqus XML.
Disqus Admin > Community > Export and click "Export".
- Save your Disqus XML file as
input/disqus.xml
(overwrite the sample file).
Map URLs to Local Template Files
The most annoying step of this process is creating your input/contentMap.json
file (there is a sample file already in place). This maps each path
of each Disqus comment thread to it’s eleventy template file.
Example disqus.xml thread url:
<link>https://www.zachleat.com/web/23-minutes/</link>
This script will normalizes to the URL path, extracting /web/23-minutes/
as the key (it will remove query parameters and de-dupe those threads). In my case, this url maps to ./_posts/2017-11-21-23-minutes-font-loading.md
. Each comment thread will need a map entry.
Open up input/contentMap.json
to see an example.
You can use an eleventy
template on your existing blog to generate this file for you! Try this contentMap.njk
template:
---
permalink: contentMap.json
layout: false
---
{
{%- for post in collections.all %}
"{{ post.url }}": "{{ post.inputPath }}",
{%- endfor -%}
}
You may have to modify the output a little bit but that’ll get you most of the way.
Run this script
# Install the dependencies
npm install
# Generate the comment JSON files
npm run default
This will create a bunch of files in output/
.
commentsCounts.json
Copy this file into your eleventy
blog’s ./_data
folder so that it’ll be available as the commentsCounts
global.
I use commentsCounts
to show comment counts on my home page big list of blog posts (on zachleat.com). Here’s my liquid
template for that:
{% for post in collections.posts %}
{%- if commentsCounts[post.url] > 0 %}<span title="{{ commentsCounts[post.url] }} comment{% if commentsCounts[post.url] != 1 %}s{% endif %}">📢 {{ commentsCounts[post.url] }}</span>{% endif -%}
{% endfor %}
Individual template comments
The rest of the JSON files in output
should have very similar names to your blog post entries. For example, 2017-11-21-23-minutes-font-loading.md
will use 2017-11-21-23-minutes-font-loading.json
. We want to copy these files to be in the same directory as our template files. In my case, this meant the _posts
folder.
_posts/
2017-11-21-23-minutes-font-loading.md
2017-11-21-23-minutes-font-loading.json
Eleventy will now make this JSON data available automatically in our markdown template (these are known as Template Specific Data Files).
Here’s how I used it (for maximum re-use, put it in a layout template—mine is _includes/layouts/post.liquid
—otherwise you can use this on any template that might have one of our Disqus JSON comment files):
<div class="static-comments">
<!-- `id` for direct link to Comments section -->
<h2 id="comments">
{{ disqus.commentCount }} Comment{% if disqus.commentCount != 1 %}s{% endif %}
</h2>
{% for comment in disqus.comments %}
{% include comment-entry.html %}
{% endfor %}
</div>
Note that each Disqus JSON comment file has a top level disqus
key in the object. We are referencing that in the template.
And then comment-entry.html
file in _includes/
:
<div class="static-comments-reply" id="comment-{{ comment.postId }}">
<div class="static-comments-hed">
<img src="{{ comment.avatar }}" class="static-comments-img">
<h3 class="static-comments-title">{{ comment.author }}</h3>
<em class="static-comments-date"><a href="#comment-{{ comment.postId }}">{{ comment.date }}</a></em>
</div>
<div class="static-comments-msg">{{ comment.message }}</div>
{% for reply in comment.replies %}
{% include comment-entry.html, comment: reply %}
{% endfor %}
</div>
I using comment-postId
as the linkable ID for each individual comment to reuse the same convention that Disqus uses (it’ll maintain existing links to comments).
For good measure, here’s the CSS I used:
.static-comments-reply {
margin: 1em 0 3em;
}
.static-comments-reply > .static-comments-reply {
margin-top: 2em;
/* This is where the threading magic happens */
padding-left: 1.5em;
border-left: 4px solid #eee;
}
.static-comments-title {
float: left;
margin: 0;
}
.static-comments-img {
float: left;
max-width: 30px;
margin-right: 1em;
border-radius: 50%;
}
.static-comments-msg {
clear: both;
line-height: 1.7;
margin-top: 1em;
}
.static-comments-date {
float: left;
clear: left;
font-size: 0.8125em; /* 13px /16 */
}
@media (min-width: 25em) { /* 400px */
.static-comments-date {
float: right;
clear: none;
}
}
/* Clearfixes */
.static-comments-hed:before,
.static-comments-hed:after,
.static-comments-msg:before,
.static-comments-msg:after {
content: " ";
display: table;
}
.static-comments-hed:after,
.static-comments-msg:after {
clear: both;
}