How to add comments to a Hugo site
Add EchoThread comments to a Hugo static site. Partial templates, per-post threads, theming, front-matter toggles, and multilingual setups.
On this page
Why Hugo + EchoThread
Hugo is one of the fastest static site generators available, but static sites have no built-in backend for comments. Most Hugo users either skip comments entirely or resort to heavyweight third-party services that inject ads and tracking scripts. For a side-by-side look at the available options, see the best commenting system for static sites.
EchoThread is built for exactly this use case: a lightweight, privacy-first comment widget that loads from a CDN with zero dependencies. The entire widget is around 37 KB gzipped, so it won't slow down your blazing-fast Hugo build.
Prerequisites
- A Hugo site (any version)
- An EchoThread account — create one free
- Your site's API key from the EchoThread dashboard
Step 1 — Create the partial
Create a new file at layouts/partials/echothread.html:
<!-- EchoThread comments widget -->
<div id="echothread"
data-api-key="YOUR_API_KEY"
data-page-url="{{ .Permalink }}"
data-identifier="{{ .File.UniqueID }}"
data-page-title="{{ .Title }}"></div>
<script src="https://cdn.echothread.io/widget.js" async></script>
Replace YOUR_API_KEY with the API key from your EchoThread dashboard.
Step 2 — Include in your layout
Open your single post layout — usually layouts/_default/single.html or layouts/posts/single.html — and add the partial where you want comments to appear:
{{ define "main" }}
<article>
<h1>{{ .Title }}</h1>
{{ .Content }}
<!-- Comments -->
{{ partial "echothread.html" . }}
</article>
{{ end }}
That's it — comments will now appear on every page that uses this layout.
Choosing an identifier
The data-identifier attribute determines which comment thread is loaded. If you change your URL structure later, the identifier keeps comments attached to the right page.
Hugo gives you several good options:
{{ .File.UniqueID }}— a hash of the file path. Stable as long as you don't move the file.{{ .RelPermalink }}— the relative URL, e.g./blog/my-post/.{{ .Params.comment_id }}— a custom front matter field you control entirely.{{ .File.ContentBaseName }}— the filename without extension, e.g.my-post.
{{ .File.UniqueID }} for most sites. If you use a custom field like comment_id, you get full control but need to remember to set it in every post's front matter.
Theming
EchoThread follows the visitor's OS preference (light/dark) by default. You can override this with the data-theme attribute:
<!-- Force dark mode -->
<div id="echothread"
data-api-key="YOUR_API_KEY"
data-theme="dark"></div>
<!-- Custom background color (text adapts automatically) -->
<div id="echothread"
data-api-key="YOUR_API_KEY"
data-theme="#FAF7F2"></div>
You can also set a custom accent color for buttons and links:
<div id="echothread"
data-api-key="YOUR_API_KEY"
data-accent-color="#2563eb"></div>
data-theme="{{ .Site.Params.echothread_theme | default "auto" }}" and configure it in hugo.toml under [params].
Disable per page
Wrap the partial in a conditional so you can disable comments on specific pages via front matter:
{{ if ne .Params.comments false }}
<div id="echothread"
data-api-key="YOUR_API_KEY"
data-page-url="{{ .Permalink }}"
data-identifier="{{ .File.UniqueID }}"
data-page-title="{{ .Title }}"></div>
<script src="https://cdn.echothread.io/widget.js" async></script>
{{ end }}
Then in any post's front matter, set comments: false to hide the widget:
---
title: "About Me"
comments: false
---
Multilingual sites
Hugo's multilingual mode creates separate URL trees per language. EchoThread automatically creates a unique thread per URL, so each language version of a post gets its own comment thread.
If you want to share comments across translations, use a language-independent identifier:
data-identifier="{{ .File.TranslationBaseName }}"
This uses the filename (without the language suffix), so my-post.en.md and my-post.fr.md will share the same thread.
Troubleshooting
Comments don't appear
- Check that your API key is correct — you can verify it in the dashboard.
- Make sure the domain in your EchoThread site settings matches your Hugo site's domain (including
localhostfor local dev). - Open your browser's developer console and look for network errors on the
widget.jsrequest.
Comments load on the wrong page
This usually means data-identifier is the same across multiple pages. Verify the identifier is unique per page by inspecting the rendered HTML.
Widget doesn't match my theme
Use data-theme to match your Hugo theme's background. For full control, override the CSS custom properties on .et-widget. See the theming docs for all available variables.
Using another static site generator?
EchoThread has dedicated guides for every major SSG. See how to add comments to an Astro blog for the component-based approach, or the platform guides index for Jekyll, Eleventy, Gatsby, and Next.js.
Frequently asked questions
Does Hugo support comments natively?
No. Hugo generates static HTML and has no backend, so comments need an external service. EchoThread adds them with a single layouts/partials/echothread.html partial and one script tag — there is no server or database for you to run.
Will a comment widget slow down my Hugo site?
The EchoThread widget is around 37 KB gzipped, loads from a CDN, and is requested with the async attribute, so it loads after your content and does not block rendering. It runs at the browser, so it has no effect on your Hugo build time.
How do I keep comments attached to a post if its URL changes?
Set a stable data-identifier such as {{ .File.UniqueID }} or a custom front-matter field. EchoThread keys the thread to that identifier rather than the URL, so comments stay with the post even if you change your permalink structure.
Ready to add comments to your Hugo site?
Free for your first site. Set up in under 5 min.
Create free account