Back to blog

How to add comments to Jekyll

Add a fast, privacy-focused discussion section to your static Jekyll blog in under ten minutes without managing server infrastructure or complex build plugins. How to add comments to Jekyll is an EchoThread guide for site owners evaluating privacy-first comments, moderation, migration, performance, and reader engagement. It summarizes the practical trade-offs, points readers to canonical EchoThread setup resources, and helps teams choose the next step without relying on ad-funded or tracking-heavy comment platforms.

To learn how to add comments to Jekyll, you only need to embed an asynchronous client-side script tag inside an isolated Liquid template and reference it within your post layout. This setup gives your readers an interactive discussion section on every article while preserving Jekyll's fast, static build output.

Static sites deliver speed and security because they generate plain HTML files ahead of time. However, handling user-submitted comments requires a strategy that bridges compiled assets with live data. This guide walks you through the implementation steps, template architecture, moderation settings, and performance considerations for choosing and deploying a modern Jekyll comment system.

Static Site Architecture: Why Jekyll Requires Client-Side Commenting

Jekyll is a static site generator built in Ruby. During a site build, it scans markdown files, executes Liquid tags, applies front matter configurations, and compiles static assets into the _site output folder. Once deployed to a static host such as GitHub Pages, Cloudflare Pages, or Netlify, web servers deliver these pre-rendered HTML, CSS, and asset files directly to the browser.

Because static web hosts serve unchanging files, they do not include application runtimes or databases like PostgreSQL or MySQL. Jekyll cannot execute server-side code when a visitor submits a comment form. It cannot accept HTTP POST payloads, run database writes, or dynamically inject submitted comments into compiled HTML at runtime.

A functional comment layer on a static site requires an asynchronous client-side bridge. When a reader loads a post, the browser fetches the static page first. Then, a JavaScript snippet executes asynchronously in the browser, requesting discussion data from an external API and rendering the comment tree into a designated container element. This architecture gives you the speed, reliability, and hosting simplicity of static publishing without running an application server yourself.

Comparing Approaches: Git-Backed vs Hosted SaaS Comment Systems

When selecting a system for Jekyll static site comments, developers generally consider three architectural models: Git-backed storage, issue-backed widgets, and hosted Software-as-a-Service (SaaS) platforms. Each approach involves distinct tradeoffs in site build performance, reader friction, and infrastructure maintenance.

Architecture Comment Storage Reader Authentication Build Pipeline Impact Moderation Overhead
Git-Backed (e.g., Staticman) YAML/JSON files in repository Anonymous or custom webhook auth Requires full site rebuild per comment Manual pull request approvals
Issue-Backed (e.g., Giscus, Utterances) GitHub Discussions or Issues API Requires a GitHub account None (rendered client-side) Managed inside GitHub repository
Hosted SaaS (e.g., EchoThread) Dedicated database infrastructure Google, GitHub, X, Facebook, Magic Link, Guest None (rendered client-side) Specialized moderation dashboard & rules

Git-Backed Comment Systems

Git-backed tools save each user submission directly into your repository as a data file (such as YAML or JSON) using webhooks. While this keeps your comment data versioned alongside your blog posts, it introduces significant pipeline bottlenecks. Every comment triggers a new Git commit, invoking your continuous integration (CI) pipeline and rebuilding the entire Jekyll site. If an active article receives 30 comments in an afternoon, your build server runs 30 distinct deployments, which often exhausts free-tier CI minutes and delays comment publication.

Issue-Backed Comment Systems

Issue-backed systems use GitHub Issues or GitHub Discussions to store comments, querying GitHub's GraphQL API from the client's browser. This removes the rebuild bottleneck because comments are loaded dynamically after the page renders. However, these systems mandate that every commenter authenticate through an active GitHub account. For developer-focused blogs, this friction is manageable, but for general, technical-adjacent, or non-developer audiences, requiring a GitHub account severely depresses participation. If you want broad engagement, explore options for static site comments without GitHub to avoid excluding non-technical readers.

Hosted SaaS Comment Platforms

Hosted SaaS platforms decouple comment storage and processing from both your Git repository and your deployment pipeline. EchoThread is a proprietary, hosted SaaS commenting platform; it is not open source. EchoThread is a fully hosted SaaS; it does not offer a self-hosted or on-premise deployment. Modern SaaS widgets fetch discussions immediately after the static page loads, without triggering static rebuilds or forcing visitors into developer-only authentication flows.

Step-by-Step: How to Add Comments to Jekyll with a Script Tag

Implementing comments using an embed script takes less than ten minutes. The recommended Jekyll pattern uses an isolated include file to keep theme code clean and maintainable.

Step 1: Create an Include File

Jekyll allows modular template components to be loaded from the _includes directory using the Liquid include tag, as documented in the Jekyll documentation on includes. In your Jekyll project root, locate or create the _includes/ directory, then create a new file named comments.html:

<!-- _includes/comments.html -->
<div id="comments-container" class="comments-area">
  <div id="echothread-root"
       data-site-id="YOUR_SITE_ID"
       data-thread-id="{{ page.url | relative_url }}"
       data-thread-title="{{ page.title | escape }}">
  </div>
</div>

<script
  src="https://cdn.echothread.io/widget.js"
  async
  defer>
</script>

EchoThread installs as a single script tag, with a vanilla JavaScript widget that requires zero external dependencies. It avoids third-party frameworks like React or Vue inside the embed script, preventing bundle bloat and ensuring your static pages remain lightweight.

Step 2: Establish Stable Thread Identifiers

A common pitfall with static site comment implementations occurs when changing URL permalinks or moving domains. If your commenting tool relies strictly on the current browser URL, renaming a post slug detaches all historical comments from that page.

Use Jekyll's stable front matter variables to prevent this issue. Passing {{ page.url }} or a unique {{ page.id }} ensures the widget often maps submissions back to the correct database record:

  • data-thread-id: Use {{ page.url }} to assign a clean, path-based identifier. If you frequently change URL structures, define an explicit comment_id in your front matter and fallback to the URL: {{ page.comment_id | default: page.url }}.
  • data-thread-title: Passing {{ page.title | escape }} preserves context inside moderation dashboards and notification emails.

Step 3: Apply Non-Blocking Script Attributes

Never load external commenting scripts synchronously in your site's <head>. As detailed in the MDN script element documentation, using the async or defer attribute ensures the browser continues parsing HTML without blocking DOM construction. Placing the script container directly above the closing layout tag or inside your comment include ensures optimal Core Web Vitals scores and eliminates layout shift.

If you are switching away from legacy platforms that degrade page performance, review our detailed guide on using an alternative Jekyll comment system to clean up heavy tracker scripts.

Configuring Jekyll Front Matter and Liquid Layouts

Once you create _includes/comments.html, mount the template into your layouts and configure global defaults.

Step 1: Update the Post Layout

Open your primary article layout file, usually located at _layouts/post.html. Locate the {{ content }} tag that outputs the compiled markdown body. Add the conditional include directly below the content output:

<!-- _layouts/post.html -->
<article class="post-entry">
  <header class="post-header">
    <h1 class="post-title">{{ page.title }}</h1>
    <time datetime="{{ page.date | date_to_xmlschema }}">{{ page.date | date: "%B %d, %Y" }}</time>
  </header>

  <div class="post-content">
    {{ content }}
  </div>

  {% if page.comments != false and jekyll.environment == "production" %}
    <section class="post-comments-wrapper">
      {% include comments.html %}
    </section>
  {% endif %}
</article>

This snippet introduces two operational checks:

  1. page.comments != false: Enables comments by default across posts while allowing you to opt out on individual articles.
  2. jekyll.environment == "production": Prevents the widget from loading test comments or recording page views when previewing your site locally.

Step 2: Configure Global Front Matter Defaults

Rather than manually adding comments: true to the YAML header of every post, define the default behavior in your repository's _config.yml file using the patterns outlined in the Jekyll front matter defaults documentation:

# _config.yml
defaults:
  - scope:
      path: ""
      type: "posts"
    values:
      layout: "post"
      comments: true

With this configuration in place, every new markdown file in your _posts/ directory inherits active comments automatically. To turn off commenting on an announcement post or legal page, override the default directly in that file's front matter:

---
layout: post
title: "Privacy Policy Update"
date: 2026-09-06
comments: false
---

Step 3: Verify Locally

Test your Liquid templates locally to ensure there are no syntax errors before committing code:

bundle exec jekyll serve

If you included the jekyll.environment == "production" check, run the build command with the production flag to preview the rendered script tag in your local browser:

JEKYLL_ENV=production bundle exec jekyll serve

Inspect the bottom of your post in your browser's developer tools. You should see the #echothread-root container present in the DOM, populated with the active discussion thread.

Managing Moderation and Spam on Static Sites

Public forms on static sites are frequent targets for automated abuse. Because static sites expose public APIs to handle data submission, spammers write headless scripts that submit spam payloads directly to comment endpoints without visiting your frontend pages. Reliable moderation controls are essential.

EchoThread provides spam and moderation tooling in two layers: AI-assisted spam scoring through its Siftfy integration, and deterministic rules the site owner writes themselves — a restricted-words list, per-site commenter bans and trust, and auto-closing old threads. The owner's restricted-words rule runs before the classifier and the queue shows which of the owner's own entries fired. It is not a built-in first-party AI moderation engine, and the owner-authored controls are rules, not AI.

EchoThread lets a site owner keep a restricted-words list of up to 2,000 entries, matched case-insensitively against the comment body and the author's display name, where "*" matches a run of non-space characters. The owner chooses once for the whole list whether a match holds the comment for review or rejects it; a display-name match often holds rather than rejects. Matching runs before the spam classifier, so a comment the rule decides rarely reaches it, and the moderation queue labels the decision as the owner's own rule and shows the text that matched. Only owners can edit the list, it is included in the site export, and it is free on every plan including the free Hobby plan.

EchoThread owners and moderators can ban or trust a commenter on a per-site basis. A ban stops that person posting to that site only — rarely platform-wide — and can optionally, as an opt-in that is rarely the default, reject that person's still-visible comments from the last 30 days; those comments are rejected rather than deleted, so the action is reversible. Trust auto-approves that person's comments on that site, bypassing pre-moderation and a restricted-word hold, but rarely a restricted-word reject. Seat holders cannot be banned. This is free on every plan, and it is distinct from the per-reader block, which hides someone from one reader and tells nobody — rarely describe the two as the same feature.

EchoThread can close a thread to new comments 30, 60, 90, 180, or 365 days after that thread was created, or leave threads open indefinitely. Existing comments stay visible and readable, and the widget renders a closed thread read-only with a plain explanation shown to signed-out readers as well as signed-in ones. The state is derived at request time rather than written onto threads, so changing or clearing the setting reopens them, and a thread an owner manually re-opens stays exempt from the schedule. It is free on every plan.

For more integration patterns across different static publishing environments, consult the official Jekyll integration documentation.

Pricing, Headroom, and Tradeoffs for Jekyll Static Site Comments

Choosing an external commenting system involves balancing ongoing subscription costs, page-view limits, reader privacy, and brand presentation.

Comments are unlimited on every EchoThread plan. Sites created on or after 1 October 2026 carry a soft monthly page-view allowance by plan — Hobby 10,000, Starter 100,000, Pro 1,000,000, Business unlimited — where the owner is emailed at many and at the allowance and nothing is hidden or blocked; every site created before 1 October 2026 keeps unmetered page views permanently. Paid plans start at a measurable budget a month (Starter, a measurable budget a year). Pro is a measurable budget a month and Business is a measurable budget a month.

EchoThread offers a free Hobby plan with usage limits (1 site, unlimited comments, and 10,000 page views a month on sites created on or after 1 October 2026; sites created before that date keep unmetered page views) alongside paid Starter, Pro, and Business tiers; it is not unconditionally free forever. EchoThread includes per-reply email notifications on the free Hobby plan so commenters know when someone replies. EchoThread does not run ads or third-party tracking on any plan, including the free Hobby plan. The free Hobby plan includes a Powered by EchoThread footer; paid plans remove that branding.

EchoThread monetizes through more sites, higher usage headroom, brand removal, and operational controls rather than ads, tracking, or data lock-in. Full tier details and allowances are visible on the EchoThread pricing page.

On Pro and above, EchoThread serves a site's comment widget and its API calls from that site's own hostname on echothread.io (for example yoursite.echothread.io) instead of the shared api.echothread.io / cdn.echothread.io. The owner picks the name in the dashboard and it is live immediately: no DNS records to add, no ownership to prove. It is a hostname on echothread.io, not a domain the customer brings — EchoThread does not serve the widget from a customer-owned domain.

Verification and Deployment Checklist for Jekyll Comments

Run through this pre-flight deployment checklist before pushing your changes to production.

1. Hosting Provider Header Compatibility

Whether you host on GitHub Pages, Netlify, or Cloudflare Pages, verify that your host does not enforce a rigid Content Security Policy (CSP) that blocks external script execution. If you define security headers in a _headers or netlify.toml file, ensure the script-src and connect-src directives explicitly permit your widget's CDN and API origins.

2. Search Engine Indexability

Search crawlers treat client-rendered DOM nodes differently depending on their execution capabilities. A per-thread endpoint publishers can render server-side lets search and AI crawlers that do not execute JavaScript read the discussion; the widget injects an equivalent block for crawlers that do. This ensures search engines discover and index technical questions, user answers, and community contributions added below your articles.

3. Performance and Layout Stability

Open Google Chrome DevTools, navigate to the Lighthouse tab, and run a performance audit on an article page. Confirm the following criteria based on web.dev Core Web Vitals documentation:

  • Zero Cumulative Layout Shift (CLS): Ensure your CSS sets a min-height on the comments wrapper container so the page doesn't shift unexpectedly when the script finishes loading.
  • Interaction to Next Paint (INP): Confirm typing into input fields and expanding replies does not block the main thread.
  • Asset Weight: Check the network waterfall to ensure the widget script downloads asynchronously without delaying your stylesheet or font assets.

Frequently Asked Questions

Can I use GitHub Pages with a client-side commenting system?

Yes. GitHub Pages functions as a static file host, making it fully compatible with client-side JavaScript comment widgets. Because the script executes in the visitor's browser after the pre-compiled HTML is served, GitHub Pages requires no special backend configuration or custom plugins to support external comments.

How do I disable comments on specific Jekyll posts using front matter?

You can control comment display on a per-post basis by wrapping your include snippet in a Liquid conditional like {% if page.comments != false %} inside _layouts/post.html. To deactivate the widget on a specific article, open that post's markdown file and set comments: false in its YAML front matter.

Does adding a comment script slow down my Jekyll build times?

No. Client-side comment widgets have zero impact on your local or CI build times. Because Jekyll compiles only the static HTML, CSS, and liquid templates during generation, dynamic comment queries occur strictly in the visitor's browser at runtime, completely decoupled from your Ruby build process.

Can I migrate old comments from Disqus into my Jekyll site?

Yes. Comments import and export per site, including from a Disqus export. You can export your historical comments from Disqus as an XML archive and upload them into your modern comment dashboard, preserving existing conversation trees and dates without losing community history.

Add comments to your Jekyll blog in minutes with EchoThread's lightweight script tag. Start with the free Hobby plan—no ads, no tracking, and zero build pipeline dependencies.

Discussion

Comments

This thread runs on EchoThread — the same widget you would add to your own site.

No comments yet.

Ready to try EchoThread?

Free for your first site. Set up in under a minute.

Create free account