Beginner's Mind


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

  1. Create a Firebase project → add a Web app → copy the config snippet.
  2. Enable Firestore (production mode).
  3. Drop the config into config.toml so 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:

  1. It chooses a thread ID in this order: explicit shortcode parameter, commentsId front matter, file path, permalink.
  2. It loads Firebase once per page using a .Scratch flag.
  3. It calls addDoc(collection(db, "comments", thread, "entries"), {...}) and listens with onSnapshot so 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 commentsCount partial 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…