Spreadsheets and Social Proof

as the saying goes, "the best way to attract a crowd is with a crowd."

most businesses agree that customer testimonials are great marketing collateral, and luckily Fomo has over 150 of them.

but how do we show them off?

after all, reviews can be hosted on Capterra, G2 Crowd, Shopify, Wordpress, BigCommerce...

so to remove the pain of evaluating Fomo from disparate platforms, last week we launched a static HTML page with all our reviews, combined.

beyond the initial coding and copy/pasting reviews from various websites, i had to...

  1. 'build' our frontend,
  2. follow Git flow to merge my feature branch
  3. wait for 100s of tests to pass
  4. deploy to our production server

what a drag.

if someone writes a review, we should be able to showcase it in minutes, not hours.

below is our new setup, which lets us share testimonials in < 30 seconds.

step 1 - aggregate reviews

forget scraping. with a mixed-drink-in-hand i manually copy/pasted 150+ Fomo reviews.

fomo reviews google sheet

note the column headers. later you'll see how we use blank vs filled cells to customize the look of each testimonial.

step 2 - connect Google Sheets

while it's possible to configure this from scratch, i chose Blockspring for their Javascript library that makes it effortless to interact with Google Sheets data.

  {"file_id": 'AAA', "worksheet_id": 'BBB', "has_header": true}, function(res){
    // do something with spreadsheet data here

step 3 - delete everything

in the first implementation, our testimonials page had around 4,000 lines of HTML, with only ~20 of those being unique.

duplicate html code

as you can see above, everything was copy/pasted around 150 times. #lolz

after saving just one of the 'review' blocks, i removed all of this code.

step 4 - create a template

we already had markup for the rectangular testimonial, bolded title, backlink, and case study blurb, but it was pretty hacky.

fomo review example

for example, if a review was on the left vs the right side of the screen, an entire block of HTML was dedicated to that slight cosmetic difference.

if a testimonial didn't have an attributable name, you guessed it: more custom HTML.

to solve this i added the popular Handlebars library, by pasting this in the <head> section:

<!-- other stuff -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.10/handlebars.min.js"></script>
<!-- more stuff -->

next, i rewrote our testimonial "div" markup.

<div class="testimonials__item">
  <p>{{ body }}</p>

  <p class="text-small-caps pt12 h6">
    {{#if author_name }}<strong>- {{ author_name }},</strong>{{/if}}
    <a class="text-orange" alt="{{ company_name }}" href="{{ company_website }}">
      {{ company_name }}

instead of duplicate HTML blocks, conditional formatting now handles the presence/absence of case study elements and backlinks.

{{#if right}}<div class="col-md-4"></div>{{/if}}

{{# if case_study}}
  <div class="case-study">
    <a href="{{ case_study }}>
      <img src="/landing/images/customers/case-study.png">

with this in place, we're ready to combine review content with our markup.

step 5 - fetch and loop reviews

when a visitor loads the reviews page, the following happens:

  1. Handlebars initializes and attaches to the template above
  2. Blockspring fetches all rows (reviews) from our spreadsheet
  3. jQuery appends review data to the HTML elements with Handlebars '{{ }}' syntax

Fomo customer testimonials

step 6 - improving the user experience

it takes Blockspring 2-3 seconds to fetch every review from our Google Sheet, which can give the impression that our Customers page just has a few logos at the top.

to improve expectations (and request patience) from our visitors, i added one more template:

IMG OF 'loading' box

this 'loading' widget is displayed synchronously with the other static page content, and then hidden before the first dynamically fetched review is appended to the feed.

speaking of dynamic content, tactically adding reviews to a Google Sheet takes a moment since we need to provide values for the author name, company backlink, and case study cells.

to handle this, i added logic to ignore reviews with blank 'body' (caption) parameters.

our fetch & display strategy now looks like this:

var source = document.getElementById("review-template").innerHTML;
var template = Handlebars.compile(source);
var reviewFeed = $('.testimonial-container');

  {"file_id": 'AAA', "worksheet_id": 'BBB', "has_header": true}, function(res){

    var reviews = res.params.data;
    $('.testimonial-placeholder').hide(); // remove placeholder

    $(reviews).each(function(i) {
      var review = reviews[i];

      // format which side of the screen
      if (i & 1) {review.left = true;} else {review.right = true;}

      // prevent blank review divs from incomplete spreadsheet entry
      if (review.body && review.body.length > 1) {


with the implementation above, showcasing new Fomo customer reviews is as simple as adding a row to a spreadsheet.

no more git flow, code deploys, unit tests, node builds, or duplicate code.

to see your company featured on our website, leave us a review and we'll get it done. quick.