Building a Comment System for my Blog
Over the weekend I went hunting for comment widgets and none of them matched what I wanted, so I hacked together a Firebase comment widget myself. It lives in the repo, deploys with git push, and meets my four must-haves: totally free, no signup wall, lightweight to drop into any post, and something I could build while vibe coding with a coffee. If you’re also searching for that combo, feel free to copy this setup.
Why roll my own?
- Free – Firebase’s tier is more than enough for my tiny site.
- No signup – traffic is small, so spam isn’t a big concern; I care more about lowering friction.
- Light + easy – it’s just a Hugo shortcode plus a bit of JS.
- Fun to build – I’d rather glue it myself than depend on another SaaS.
1. Wire up Firebase + Firestore
- Create a Firebase project → add a Web app → copy the config snippet.
- Enable Firestore (production mode).
Drop the config into
config.tomlso Hugo can hand it to the shortcode:[params.comments] enabled = true provider = "firebase" collection = "comments" [params.firebase] apiKey = "AIza..." authDomain = "my-site.firebaseapp.com" projectId = "my-site" storageBucket = "my-site.appspot.com" messagingSenderId = "123456789" appId = "1:123456789:web:abc123"
2. Build a Hugo shortcode
layouts/shortcodes/firebase-comments.html renders the form, loads Firebase modules, and writes to Firestore. The interesting parts:
- It chooses a thread ID in this order: explicit shortcode parameter,
commentsIdfront matter, file path, permalink. - It loads Firebase once per page using a
.Scratchflag. - It calls
addDoc(collection(db, "comments", thread, "entries"), {...})and listens withonSnapshotso the UI updates immediately.
With that in place, each post gets its own Firestore subcollection like comments/building-a-comment-system-for-blog/entries/*.
3. Drop the shortcode into posts
## Join the discussion
{{< firebase-comments >}}
That’s it. Hugo just substitutes the widget wherever I put the shortcode.
4. Lock down Firestore rules
Anonymous forms are spam magnets, so I added just enough validation:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /comments/{thread}/entries/{comment} {
allow read: if true;
allow write: if
request.resource.data.keys().hasOnly(["name","message","createdAt"]) &&
request.resource.data.name is string &&
request.resource.data.name.size() > 0 &&
request.resource.data.name.size() <= 80 &&
request.resource.data.message is string &&
request.resource.data.message.size() > 0 &&
request.resource.data.message.size() <= 1200 &&
request.resource.data.createdAt == request.time;
}
}
}
That keeps field names predictable, enforces length limits, and forces comments to stamp serverTimestamp().
Next steps
I’ll layer on polish only after real comments start appearing:
- Email notifications via a tiny Cloud Function.
- A
commentsCountpartial so the posts list shows activity. - Cloudflare auth/Turnstile if spam ever becomes a thing.
- Reply support if threads get lively.
For now, the bar was “ship something free, anonymous-friendly, and easy to reuse.” Mission accomplished—and it was a fun little weekend build.
Comments
Loading comments…