The problem

I’m a cheap thrifty dev. I will pay for things when it’s worth it, but I always like to explore the free options to see how creatively I can solve my own problems without paying someone else to solve them for me.

Oh, Kevin. Saying what we all were thinking since 2005.

Comments for online blogs require some sort of back-end hosting to store the comments. Some of the more common solutions, like Disqus, are widely used but are not without their issues. There are self-hosting options, but then self-hosting your own services always brings more upkeep than the average dev (or side project) normally has room for.

So what to do for a tight-pocketed fella?

Inspiration: This guy’s article (code maintained here) inspired I believe by this Github repo.

The gist: Google sheets are the best (only?) mass hosted, reliable, back-end like service that doesn’t upsell you to a paid option. Check this out for a recent article on sheets as a database, with some sample node.js code for doing so. Using google sheets has limitations as opposed to a true back-end database (no locking or atomic options, can get slow as the data grows, keeping the sheets private means struggling with the less-than-friendly google APIs), but these are perfectly reasonable given what is offered for free.

The solution: Create a google sheet to act as a comments database, which can be written to and read from on loading of blog posts. Now I am not a huge fan of javascript, python being my language of choice. So rather than using the very elegant, but javascript heavy posting solutions referenced above (sending POST requests to your Google Form URL), I personally like to take advantage of what’s on offer from the fine folks at Tally.so to minimize the overall amount of JS I have to fight with.

Tip

🔥 Tally.so is a really great example of internet products done right. They offer an incredible product and give more of it away for free than they frankly should. You should subscribe, if only to support such a great team and product (and if you want to remove their branding and customize the CSS of their forms).

Using the incredibly generous tier at Tally, you can skip the javascript part of hooking up and submitting requests to a Google form by creating a comment section Tally form and connecting it to your google sheet. You can even customize the CSS background to make it transparent (I use HTML hex code #FFFFFF00), and then embed it right into your page, along with a custom ‘Thank you’ snippet for submitting a comment.

Part 1 - Creating the Tally Form and Google Spreadsheet

(1) Once you’ve created a free account on Tally.so, you’ll be brought to your ‘Dashboard’. You can click on the “+ Create Form” button in the top right of the screen to make your first form.

Click the “+ Create form” button in the top right of the screen

(2) Design your form however you would like your comment submission form to look. The interface is ‘block-based’ and works a lot like the popular tool ‘Notion’, if you’ve ever used it. You can bring up a menu of different block upons by typing the ‘\’ key. I’ve made all of the fields required (I believe they are by default), and included a ReCaptcha element to filter out spam. I’ve made the form transparent so it fits nicely into whatever page I will embed it in by clicking on the ‘Design’ tab, and then on the right choosing ‘Custom’ and making the value for ‘Background: #FFFFFF00”. You can also customize what the form says upon submission by adding a ‘Thank You’ page, which is one of the menu options for blocks to add. When you’re happy, finished, click ‘Publish’.

Design your comment submission form.

(3) As soon as you hit ‘Publish’, you’ll be taken to the ‘Share’ tab of your form’s admin panel. You have different options for embedding your form - I prefer to embed it as an iframe chunk (although this will have a bit of an impact on things like your page loads), and get the code by clicking on Standard. Note that you can access other tabs from your form’s admin panel like submissions, where you can track any data submitted by users to your form.

The 'Share' tab of your form's admin panel.

(4) Select ‘Standard’ and you’ll be redirected to a page with embedding instructions. You have a few options, but the simplest is to click on the ‘Get the code’ button, and copy the code to embed your form. Paste it in your blog post’s HTML page wherever you’d like the form to appear.

Click on 'Get the code' to access the iframe code.
Copy the embed code and then paste that into your page's HTML wherever you'd like the form to appear.

(5) Lastly, you’ll need to integrate the form with a Google sheet to fetch the comments from (using, unfortunately, javascript…). Go back to your form’s admin panel, click on the ‘integrations’tab, and click ‘Connect’ next to Google sheets. Choose the Google account you’d like to own the sheet, and then you’ll be able to access that sheet from the new spreadsheet link.

Tip

💡 Be sure to note your google sheet’s unique ‘Sheet ID’, as you’ll need it later for fetching the comments with Javascript. Your Sheet ID is in the google sheet url:

https ://docs.google.com/spreadsheets/d/SHEET-ID/edit#gid=0

Connect your Tally form to a google spreadsheet you own to host your comment data.

Okay, we’re done making and embedding the form - try it out!

Part 2 - Fetching comments and rendering them on your page (aka. the crummy javascript part)

Note

💡 If you’re like me and prefer to host your blog using the server-side language of your choice (like Python), you might just implement this part using your language’s Google Sheets API (like python’s ‘gpspread’ package). But to be language agnostic (and minimize server-side logic and loading-time), here’s a simple javascript example.

(1) Make your blog post’s google sheet public so we don’t have to use the Google API, but make sure the public only have ‘Viewer’ privileges. Open up your sheet, click the green ‘Share’ button in the top right, and change ‘Restricted’ to ‘Anyone with the link’ under the ‘General Access’ heading, making sure it then says ‘Viewer’ to the right of that.

Warning

This obviously is a solution for blog posts that are publicly available, as the google sheet will have to be publicly available. For gated content, you’ll have to make the sheet private and use the Google Sheets API, which is do-able but a bit of a pain.

Make your google sheet publicly available (with 'viewer only' access).

(2) Now the dreaded JS. You can (and should) put the following in a local .js file and import it into your HTML, but in the spirit of making this as accessible as possible, here’s the code you can use in a <script defer></script> tag, inserted as the very last part of your <head></head> section.

Ugh. Javascript.

There’s quite a bit going on there:

  • We specify the unique ‘sheet ID’ associated with the google sheet connected to your Tally.so form that’s storing your comment data,

  • we then fetch it with the fetch() api, convert the data to json-format,

  • iterate through the rows with a ‘for-loop’, and then

  • re-format the data in the ‘Date’ column to read better when rendered into the final HTML.

  • Finally, we decide what the HTML will look like style-wise, and insert it into a <div id=’comments’> element included in your blog post’s HTML where you’d like the comments to show up (usually right above where you’ve embedded your Tally form).

A weird part that took me a minute to figure out

A particularly interesting part of this was the following segment:

This took a minute to figure out.

The first row in the sheet is quite clearly the column headers. Where there were additional rows of data (ie. there were comments), the first column was ignored as we would like. Where, however, there were no additional rows of data (ie. no comments), the header row was fetched as a row of data and rendered as though it was a comment - no good! So this little snippet checks if the first cell fetched matches the first header value, and if so, exits the entire function. This is convenient because this allows us to include a message in the HTML like ‘no comments yet!’, which will be replaced by the javascript function if there are comments, but will not be replaced if the function is exited early for lack of data. Neat!

Finishing up

Your HTML should have a section that looks like this, where you’d like all the comments to go:

Don't forget to include the HTML snippet that gets replaced by the javascript!

And that’s about it! It should all be working now!

Coda: timezones…

Except…the Tally.so forms record timestamps in UTC time, and I’m in the EST. You may have noticed that my Google Sheet looks different than yours, with a fifth column. I tried the solution on Tally’s website but (at least for me) the solution didn’t work without some tweaking. The code I used in the 5th column header to adapt to my local time is as follows:

Insert as your fifth column header

This created a new column with the correct timestamps, and then I just changed this line in the javascript function:

 const date = new Date(row.c[2].v.slice(6));

to this:

 const date = new Date(row.c[5].v.slice(6));

so as to get the new dates instead of the old ones, and voila! All done! Here’s how the comments section looks in my finished blog post:

Phew! All done.

Final thoughts

It’s always interesting to hack away at free solutions. What’s nice about this is it works for essentially any forms you might want on your JAMstack site - I also use a Tally form for my contact page, and for my email subscribers list.

That said, while this all certainly works there are a number of potential downsides:

  • You have to create a new Tally form and google sheet for each blog post,

  • You have limited control over the styling of the form, and there’s a ‘Made by Tally’ stamp,

  • There’s lot of breakable parts, and everything that comes along with loading an iframe.

Overall, there may well be a good argument to use the extra javascript from the original solution that inspired this post instead (see another great example here), or a more out-of-the-box solution. Also, if your blog gets traction, you probably just want to pay/self-host a proper solution - with content creation, you really want to focus on minimizing anything and everything that gets in the way of just writing and publishing.

Thanks for stopping by!