Back to blog

How to Add Comments to 11ty

Add lightweight, privacy-focused discussion sections to your Eleventy blog using clean template partials, custom identifier metadata, and minimal client-side JavaScript. How to Add Comments to 11ty 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 add comments to an 11ty static site, you only need to create a template partial containing a lightweight JavaScript snippet and include it conditionally inside your post layouts. This guide details how to add comments to 11ty so you can launch dynamic reader discussions in minutes while preserving the speed, simplicity, and performance of your static builds.

For inbox-safety context, FTC phishing guidance recommends treating unexpected messages and requests for personal information with caution.

For privacy context, FTC guidance on how websites and apps collect and use information explains why people should be careful about where they share personal contact details.

Static site generators like Eleventy compile Markdown files and templates into pure HTML at build time. Because there is no persistent application server or database running on your host, processing user-submitted comments requires an external discussion layer. By using a modern, embeddable commenting widget, you can handle reader authentication, spam prevention, and real-time replies seamlessly without adding server management overhead to your Jamstack workflow.

Understanding 11ty Static Site Comments and Architecture Options

Choosing an architecture for 11ty static site comments comes down to balancing implementation effort, page performance, and reader accessibility. Because Eleventy builds static files, you have three primary technical approaches for handling discussions:

  • Git-backed commenting tools: Solutions that map comments to GitHub Issues or Discussions (such as Giscus) keep data in your repository. However, they require every commenter to possess an active GitHub account, creating a significant barrier for non-developer audiences.
  • Legacy ad-driven embed platforms: Older networks like Disqus offer turnkey installation, but they inject heavy tracking scripts, third-party cookies, and programmatic advertisements that degrade performance and compromise visitor privacy.
  • Hosted SaaS commenting platforms: Modern discussion systems deliver a self-contained, lightweight JavaScript widget via CDN. They handle data storage, moderation queues, and visitor authentication behind an API, letting you embed a single snippet directly into your Eleventy layouts.

When implementing an Eleventy comment integration, preserving First Contentful Paint (FCP) and low cumulative layout shifts (CLS) is critical. Heavy JavaScript bundles or complex iframe wrappers delay page rendering and harm Core Web Vitals. Selecting a modern platform built with vanilla JavaScript and zero external dependencies ensures your static pages remain snappy while supporting rich reader conversations.

Prerequisites for 11ty Comment Integration

Before modifying your templates, ensure you have your Eleventy project structured with layout templates and data cascading enabled. A typical 11ty project directory contains the following components:

my-11ty-blog/
├── _data/
│   └── site.json
├── _includes/
│   ├── layouts/
│   │   ├── base.njk
│   │   └── post.njk
│   └── comments.njk
├── posts/
│   ├── posts.json
│   ├── first-post.md
│   └── second-post.md
├── .eleventy.js
└── package.json

To connect discussions to individual posts, you need two project prerequisites:

  1. A persistent thread identifier: Each discussion thread requires a unique key so the widget knows which comments to fetch. In 11ty, the built-in page.url variable provides a reliable identifier (for example, /posts/first-post/). If you anticipate changing URL structures later, you can define an immutable thread_id or slug in your front matter.
  2. An EchoThread account: EchoThread is a proprietary, hosted SaaS commenting platform; it is not open source and it does not offer a self-hosted or on-premise deployment. You will register your site in the dashboard to obtain your unique Site ID. For complete configuration flags and API documentation, review the official Eleventy integration guide.

Step 1: How to Add Comments to 11ty Layout Templates

The cleanest way to implement how to add comments to 11ty is by isolating the widget code inside a dedicated template partial. This keeps your main layout templates organized and allows you to update comment configurations across your entire site from a single file.

Creating the Comment Partial

Create a new file named comments.njk inside your project's _includes/ directory. Paste the following vanilla JavaScript snippet, which loads the client script and sets dynamic container attributes:

<!-- _includes/comments.njk -->
<section class="comments-section" aria-label="Reader Comments">
  <div id="echothread-comments"
       data-site-id="YOUR_SITE_ID"
       data-thread-id="{{ page.url }}"
       data-thread-title="{{ title }}"
       data-thread-url="{{ site.url }}{{ page.url }}">
  </div>
  <script src="https://cdn.echothread.io/widget.js" async defer></script>
</section>

If your project utilizes Liquid templates instead of Nunjucks, create _includes/comments.liquid with identical markup:

<!-- _includes/comments.liquid -->
<section class="comments-section" aria-label="Reader Comments">
  <div id="echothread-comments"
       data-site-id="YOUR_SITE_ID"
       data-thread-id="{{ page.url }}"
       data-thread-title="{{ title }}"
       data-thread-url="{{ site.url }}{{ page.url }}">
  </div>
  <script src="https://cdn.echothread.io/widget.js" async defer></script>
</section>

Understanding the Container Data Attributes

The container element relies on specific HTML5 data attributes to map conversation threads correctly:

  • data-site-id: Your project ID from the EchoThread console.
  • data-thread-id: The unique identifier for the current page. Passing {{ page.url }} ensures that every generated HTML file automatically binds to its corresponding thread.
  • data-thread-title: The title of the post passed from your front matter, used in email notifications and moderation queues.
  • data-thread-url: The absolute canonical URL of the article, combining your global site domain with the relative page path.

Embedding the Partial into Post Layouts

Open your post layout file (for example, _includes/layouts/post.njk) and include the comment partial beneath your main article content:

<!-- _includes/layouts/post.njk -->
{% extends "layouts/base.njk" %}

{% block content %}
  <article class="post-content">
    <h1>{{ title }}</h1>
    <time datetime="{{ page.date | htmlDateString }}">{{ page.date | readableDate }}</time>
    
    {{ content | safe }}
  </article>

  <hr class="post-divider" />

  {% include "comments.njk" %}
{% endblock %}

Step 2: Adding Conditional Rendering with Front Matter

Not every page on an 11ty site requires a discussion section. You typically want comments on blog posts and tutorials, but want them suppressed on your homepage, contact forms, privacy policies, and category archives. You can manage this dynamically using 11ty's data cascade and front matter variables.

Updating the Layout Conditional

Wrap the include statement inside your layout template with a conditional check that inspects a front matter flag:

<!-- _includes/layouts/post.njk with conditional rendering -->
{% if enable_comments !== false and comments !== false %}
  {% include "comments.njk" %}
{% endif %}

This conditional ensures that comments render by default on posts unless explicitly disabled in the post's front matter:

---
title: "Announcing Our Static Site Redesign"
date: 2026-09-04
tags:
  - updates
comments: false
---

This announcement post is informational only, so comments are disabled.

Setting Global Defaults Using Directory Data Files

Rather than manually adding comments: true to dozens of Markdown files, use Eleventy's directory data files. Create a file named posts.json inside your posts/ folder:

{
  "layout": "layouts/post.njk",
  "tags": ["posts"],
  "comments": true
}

With this configuration, every Markdown file placed in the posts/ directory inherits comments: true and the post layout automatically. To turn off comments on a single post, add comments: false to that specific file's front matter to override the folder default.

How to Add Comments to 11ty Without Hurrying Page Load Performance

A static blog built with 11ty often achieves perfect 100/100 Lighthouse performance scores out of the box. Adding external scripts risks introducing layout shifts, blocking the main thread, or increasing network payload size. Following optimal browser execution strategies ensures that knowing how to add comments to 11ty does not compromise your site's delivery speed.

Non-Blocking Script Execution

Always load external client scripts asynchronously. The MDN Web Docs explain that the async and defer attributes allow external JavaScript to execute without blocking DOM parsing. Placing your script at the bottom of the page or inside the partial with these attributes ensures that your HTML text and styles render immediately before comment resources are fetched:

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

Avoiding Ad Networks and Bloated Trackers

Legacy comment platforms load megabytes of third-party tracking pixels, marketing scripts, and multi-network ad auctions. These elements cause severe layout shifts and drain mobile battery life. To keep your Eleventy site fast and compliant with global privacy standards, choose tools built on zero-tracking principles.

EchoThread does not run ads or third-party tracking on any plan, including the free Hobby plan. For technical publishers prioritizing clean code and privacy compliance, exploring a free comment system without ads or tracking provides a sustainable way to maintain sub-second page loads.

Optional: Lazy Loading on User Interaction

If your blog features long-form technical articles where comments sit far below the initial viewport, you can defer widget initialization until the reader scrolls near the bottom. You can implement this using the native browser Intersection Observer API:

<!-- _includes/comments-lazy.njk -->
<section id="comments-wrapper" class="comments-section">
  <div id="echothread-comments"
       data-site-id="YOUR_SITE_ID"
       data-thread-id="{{ page.url }}"
       data-thread-title="{{ title }}">
  </div>
</section>

<script>
  document.addEventListener("DOMContentLoaded", function () {
    const commentsWrapper = document.getElementById("comments-wrapper");
    if (!commentsWrapper) return;

    const observer = new IntersectionObserver(function (entries, self) {
      entries.forEach(function (entry) {
        if (entry.isIntersecting) {
          const script = document.createElement("script");
          script.src = "https://cdn.echothread.io/widget.js";
          script.async = true;
          document.body.appendChild(script);
          self.disconnect();
        }
      });
    }, { rootMargin: "300px" });

    observer.observe(commentsWrapper);
  });
</script>

Configuring Moderation and Spam Controls for Static Blogs

Public comment sections on static blogs inevitably attract automated bots, spam links, and promotional clutter. Because static sites lack back-end code to sanitize submissions, your commenting platform must handle moderation and security before entries appear on your live pages.

Two-Layer Spam Filtering

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.

To learn more about stopping sophisticated automated attacks across developer blogs, see our technical breakdown on how to stop AI comment spam.

Owner-Authored Restricted-Words List

Site owners can define customized validation criteria directly in the control dashboard. 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.

Per-Site Commenter Bans and Trust

Community moderation often requires managing specific problem accounts without affecting other properties. 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.

Auto-Closing Inactive Threads

Technical tutorials and release notes published on Eleventy blogs often become obsolete over time. Allowing open discussions on years-old articles creates unnecessary moderation debt. 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.

Pricing, Page View Allowances, and Production Best Practices

When selecting software for a production static site, understanding operational limits and pricing tiers prevents unexpected outages or billing spikes. EchoThread monetizes through more sites, higher usage headroom, brand removal, and operational controls rather than ads, tracking, or data lock-in.

Plan Overview and Allowances

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. Comments are unlimited on every EchoThread plan.

On sites created on or after 1 October 2026, soft monthly page-view allowances apply by plan: Hobby includes 10,000 page views, Starter includes 100,000, Pro includes 1,000,000, and Business provides unlimited page views. When a site reaches many and many its allowance, the owner receives an email notification, and nothing is hidden or blocked. All sites created before 1 October 2026 retain unmetered page views permanently.

Paid plans start at $9 a month (Starter, $90 a year). The Pro plan is $19 a month, and the Business plan is $79 a month. You can review current tier comparisons on the official EchoThread pricing page.

  • Email notifications: EchoThread includes per-reply email notifications on the free Hobby plan so commenters know when someone replies.
  • Branding: The free Hobby plan includes a Powered by EchoThread footer; paid plans remove that branding.
  • Authentication options: Readers can authenticate using Google, GitHub, X, Facebook, or magic link emails. Guest commenting without creating an account is a per-site setting that the site owner can enable or disable.
  • Subdomain isolation: 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.
  • Multilingual interface: The widget detects each visitor's browser preference to display UI elements in their native language (supporting English, Korean, Italian, and Chinese).

SEO Crawling and Indexing

Static sites often rely on discussions to generate long-tail search traffic. Traditional client-side widgets inject content via JavaScript, which standard search engine crawlers may parse with delays. To support full search visibility, EchoThread provides a per-thread endpoint publishers can render server-side that lets search and AI crawlers that do not execute JavaScript read the discussion; the widget injects an equivalent block for crawlers that do. For architectural details on how search engines process dynamic discussion threads, read our analysis on which comment systems can AI crawlers read.

Frequently Asked Questions

How do I prevent comments from loading on specific 11ty pages?

You can prevent comments from loading by using front matter variables and template conditionals. In your post layout (e.g., _includes/layouts/post.njk), wrap the include tag inside {% if comments !== false %}{% include "comments.njk" %}{% endif %}. On pages where you wish to disable discussion (such as landing pages, archives, or contact forms), add comments: false to the YAML front matter header of that specific Markdown file.

Will adding a commenting script impact my 11ty site's Lighthouse performance score?

No, provided the commenting script is lightweight and loaded asynchronously. EchoThread uses a vanilla JavaScript widget with zero external dependencies and loads via non-blocking async and defer attributes. Because it contains no third-party ad trackers, marketing pixels, or heavyweight frames, it executes without blocking the browser's main thread, preserving your site's Core Web Vitals and First Contentful Paint metrics.

Can search engine crawlers index comments on static 11ty pages?

Yes. For modern search crawlers that execute client-side JavaScript, the widget automatically injects a structured content block into the DOM. For crawlers and AI bots that do not execute JavaScript, publishers can fetch the thread's discussion content via a dedicated server-side endpoint during build time or proxy requests to ensure full text indexability.

How are discussions mapped when I change a URL or slug in Eleventy?

By default, discussions map to the data-thread-id attribute set in your template partial. If you use {{ page.url }} and later modify your permalink structure, the widget will treat the updated URL as a new thread. To prevent losing past comments during a site reorganization or permalink migration, you can define a static, permanent identifier in your front matter (such as thread_id: "2026-09-04-custom-slug") and pass that variable to data-thread-id instead of the page URL.

Ready to install discussion threads on your Eleventy blog? Create an account on EchoThread and embed the single-line script in your layout today.

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