JAMstack sites are often seen as being static. A more accurate mental model for them would be that they are sites which have the ability to be hosted statically. The difference might seem semantic, but thanks to the rise of many tools and services which simplify running a build and deploying to static hosting infrastructure, such sites can feel much fresher and dynamic than you might imagine, while still capable of being served from static hosting infrastructure, with all the benefits that brings.
A feature often used as an example of why a site cannot be hosted statically is comments. A comments engine needs to handle submissions, allow for moderation, and is by its very nature, “dynamic”.

Thanks to the growing ecosystem of tools available for JAMstack sites, there are solutions to this. Let’s look at an example which you could use on your own site, which:
- Does not depend on client-side JavaScript
- Could work with any static site generator
- Includes moderation
- Sends notifications when new comments need moderating
- Bakes the comments into your site, so that they load quickly and appear in searches
This example makes use of some of the features of Netlify, a platform for automating, deploying and hosting web projects, but many of the principles could be used with other platforms.
You can see the example site here.

Stashing our content
We’ll create 2 forms to receive all of our comments at the different stages of their journey from commenter to content. When Netlify sees a <form>
, it creates a unique data store for the data the form collects. We’ll make great use of that.
- Form 1) A queue to hold all of the new comment submissions. In other words, a store to hold all comments awaiting moderation.
- Form 2) Contains all approved comments.
The act of moderation will be somebody looking at each new submission and deciding, “yep!” or “nope!”. Those that get nope-d will be deleted from the queue. Those that are approved will be posted over to the approved comments form.

All of the comments in the approved comments form are used by our static site generator in subsequent site builds thanks to the API access Netlify gives to the submissions in our forms.

The comment form
Each page includes an HTML <form>
. By adding the boolean attribute of netlify
to any HTML form element in your site, Netlify will automatically generate an API for your form, and gathers all of the submissions to it for you. You’ll also be able to access the submissions via that API later. Handy!
The comments <form>
on each page will look a lot like this (some classes and extra copy omitted for clarity):
<form netlify name="comments-queue" action="/thanks">
<input name="path" type="hidden" value="{{ page.url }}">
<p>
<label for="name">Your name</label>
<input type="text" name="name" id="name">
</p>
<p>
<label for="email">Your email</label>
<input type="email" name="email" id="email">
</p>
<p>
<label for="comment">Your comment</label>
<textarea name="comment" id="comment"></textarea>
</p>
<p>
<button type="submit">Post your comment</button>
</p>
</form>
You’ll may notice that the form also includes a type="hidden"
field to let us know which page on the site this comment is for. Our static site generator populates that for us when the site is generated, and well use it later when deciding which comments should be shown on which page.

Submissions and notifications
When a new comment is posted via the comment form, Netlify not only stashes that for us, but can also send a notification. That could be:
- an email
- a Slack notification
- a webhook of our choosing.
These give us the chance to automate things a little.
New submissions result in a notification being posted to Slack. We’ll get to see what was submitted and to which page right there in our Slack client.

To make things extra slick, we can massage that notification a little to include some action buttons. One button to delete the comment, one to approve it. Approving a new comment from a Slack notification on your phone while riding the bus feels good.

We can’t make those buttons work without running a little bit of logic which, we can do in a Lambda function. Netlify recently added support for Lambda functions too, making the process of writing and deploying Lambdas part of the same deployment process. You’ll not need to go rummaging around in Amazon’s AWS configuration settings.
We’ll use one Lambda function to add some buttons to our Slack notification, and another Lambda function to handle the actions of clicking either of those buttons.
Bring the comments into the site
With a freshly approved comment being posted to our approved comments form, we are back to using the submission event triggers that Netlify provides. Every time something is posted to the approved comments form, we’ll want to include it in the site, so we have Netlify automatically rebuild and deploy our site.

Most static site generators have some notion of data files. Jekyll uses files in a [_data]
directory, Hugo has a similar data directory. This example is using Eleventy as its static site generator which has a similar concept. We’ll make use of this.
Each time we run our site build, whether in our local development environment or within Netlify through their automated builds, the first step is to pull all of our external data into local data files which our SSG can then use. We do that with a Gulp task.
Armed with a `comments.json` file which we have populated from a call to Netlify’s form submission API which grabbed all of our approved comments, our SSG can now build the site as normal and bake the correct comments right into the HTML of our pages.

Some benefits
Well that was fun, but why bother?
Yes, you could use something like Disqus to add comments to a statically hosted site via a few lines of JavaScript. But that adds an external JavaScript dependency and results in the comments about your content living far away from the content itself. And it’s only available once the JavaScript has loaded, pulled in the data and then injected it into your site.
Whereas this method results in comments that are baked right into the content of your site. They’ll show up in searches on Google and they will load as part of your site without the need for any client-side JavaScript.
Even better, they’ll be available for you to commit right into your code repository with the rest of your content, giving you more peace of mind and portability in the future.
The example site and all of its code are available to explore. You can try submitting comments if you like (although poor old Phil will need to moderate any comments on this example site before they appear, but that will just make him feel loved).
Better still, you can clone this example and deploy your own version to Netlify with just a few clicks. The example site explains how.
Just show me behind the scenes right now!
If you’d want to take a look at how things behave for the moderator of a site using this system without grabbing a copy of your own, this short video will walk through a comment being made, moderated and incorporated into the site.
it’s an interesting idea, however rebuilding the site or page after every new comment is approved seems kinda dumb, especially if the site is, or gets large. Would probably be better to run a web job daily to do this instead – but even rebuilding the site once a day seems silly.
I personally think content that wont change often is perfect for jam. Comments, although possible as shown, is pushing things a too far. Comments are dynamic and as such a third party plugin or a resful api solution seems much more mature. in other words jam can be mixed to provide the best outcome for the need!
Thanks Nick,
I agree with you that you might want to avoid regenerating a site for every approved comment if you have a site with a very high number of pages and a high frequency of comments. In that scenario, there is no reason that this mechanic could not be adapted to regenerate and republish less frequently.
The principle and mechanics could remain the same without bumping into the concerns you mention though, I think.
However I don’t agree that comments are dynamic and belong in a third party system. This comment, and your own, are both pertinent to this article and are best held as part of the content of this publication. Relying on a remote third party is not as desirable as retaining the comments about your content within your content.
Rendering performance, reliability, longevity, and discoverability are all improved if we can deliver the comments in the HTML of a page rather than waiting for third party JavaScript to load, fetch the content (and whatever ads they might want to inject), and then render the remaining content in the page.
It’s also nice when the content within comments surfaces in search results and bring people to your content that way.
This example was exploring what might be possible when the cost (in terms of time, money and effort) of generating and deploying a site approaches zero. Which has become the case.
Hi I’m a big fan of the Netlify stack and the easy to deploy system. However for this comment system wouldnt approving every single comment on a busy become cumbersome? How would you advise implementing a comment system without manual approval yet effective filtering for malicious comments/spam?
Thanks Kevin!
I think that’s a fair concern. Any comment system that requires moderation of all submissions will have some overhead. What I was trying to do with this example, was illustrate that with the building blocks of:
a statically generated site
automated, scripted builds based triggered by hooks
configurable notifications from form submission events
form APIs which handle your form submissions for you
ability to write and trigger serverless functions
… you have access to quite a bit of power, and you can use these tools to create all sorts of things. This is not a product, it’s a thought starter.
For many of us, including those running their own personal blogs, I think that notifications of each submission is fine if it is delivered conveniently. We’ll probably be getting that from whatever we use now anyway. But for larger sites with a much larger volume of comments, you’re right, perhaps a moderation console would be better.
This model would totally support adding a modest moderation console. We already have access to the queue of comments awaiting moderation via and API, and the serverless functions could be made to do whatever we needed. A page which shows all the comments awaiting moderation, and the ability to approve/delete them in a batch would be a simple extension to this.
Perhaps I’ll expand the example to include that too.
I’m curious, how might others extend the functionality of this example?
I feel like you could publish every comment that comes along if you wanted to and use other moderation techniques.
For spam, you could use a captcha technique or use Netlify’s spam protection in your comment form. For malicious comments, you could always moderate after the fact by deleting it from the data set and having that trigger a build, as well.
Hi Kevin. You could use a Lamda function to execute whatever filtering you wanted to implement and then push the comments that pass your automated filtering into the “approved” queue. It would be a nearly identical architecture although you should only need one Lamda instead of 2.
@philhawksworth
Hi thank you for the explanation! That makes total sense. I think managing a batch of responses is great! I see what you mean, by this article being a thought starter. Its very extendable and I’m understanding more of the possibilities now!
@bryan and @matt
Those are great ideas! I’ll definitely try to implement those filtering lambda functions and send those to a queue afterwards! I can see how using the hooks can accomplish effective filtering.
thanks all!
I think this is a non-issue. Unless you’re deploying builds on a Raspberry Pi, each rebuild should take just a few seconds at max. I have a static site powered by Hugo with about 200 posts, 100 tags, and a few categories, and it takes less than 5 seconds for it to build and upload all of it to my web server.
It could be a problem in Jekyll, where site builds can take minutes on any site with more than a handful of posts, includes or paginated pages.
Having said that, our work site is hosted on Netlify and is rebuilt 20 or so times a day. That doesn’t seem to cause problems, although I don’t know if that’d be the case if you were getting dozens of comments.
In terms of lead time from comment being made to being published, I’ve come to expect a reasonable amount of time for moderation to take place in other systems due to human intervention, and I’m comfortable with that.
If the concern is more about having many long builds waiting on Netlify, that’s not a problem as long as no individual build takes more than 30 minutes (the max allowed before timing out).
If you flood Netlify with requests for many builds, the build bots are smart enough to disregard any requests that are queued after the currently running build, except for the most recently queued build. So after the current build runs, it will skip over all the others and run the final build.
That’s just another advantage have treating all deploys as being self-contained and immutable.
@bryan, @matt
Yes you could indeed use a combination of Netlify’s Honeypot filtering, and recapture support. Both will help in reducing spam before it reaches the lambda. I just leeft those out of this example for the sake of simplicity in this example which already introduced a lot of other mechanics.
I’m personally keen in having some level of manual intervention somewhere in the moderation oipipeli, but agree that it’s good to have as much automation as possible along the way.