Black Friday: Enjoy a 35% discount on the bundles. Apply the code BLACKFRIDAY35 at checkout! Limited offer.

← Back to all tutorials

How to create an interactive testimonial with Astrojs, Tailwind CSS and JavaScript

A testimonial
Published and written on May 18 2024 by Michael Andreuzza

Recreating a testimonial section with Tailwind CSS and JavaScript that we did with Alpine.js in the previous tutorial.

What are testimonials?

Testimonials are a great way to showcase your work and build trust with potential clients. They can be used in a variety of contexts, such as a landing page, a blog post, or a product page. Testimonials can be written by your clients or by your team, and can be in the form of a quote, a video, or a written review.

Use cases:

  • Showcasing your work and building trust with potential clients.
  • Providing social proof and demonstrating your expertise.
  • Highlighting the benefits of your product or service.
  • Encouraging customer loyalty and repeat business.
  • Demonstrating the value of your services or products.

And many other reasons to use testimonials.

Understanding the code:

The testimonial text

  • style="display: none;": This is the CSS that hides the testimonial text initially.
  • lass="testimonial ...": This is the custom CSS class that styles the testimonial text.

Classes are removed for brevity, but I’ll keep those classes relevant to the tutorial.

<!-- Dynamically render testimonials -->
{
  testimonials.map((testimonial, index) => (
    <div
      class="testimonial ..."
      data-index={index + 1}
      style="display: none;">
      <p>{testimonial.content}</p>
    </div>
  ))
}

The button that will change the active testimonial which is an avatar

  • class="avatar-button ...": This is the custom CSS class that styles the button. Classes are removed for brevity, but I’ll keep those classes relevant to the tutorial.
<!-- Dynamically render name and role -->
{
  testimonials.map((testimonial, index) => (
    <button
      class="avatar-button ..."
      data-index={index + 1}>
      <img
        src={testimonial.imageUrl}
        alt="#_"
      />
    </button>
  ))
}

The tesmonial info

  • class="testimonial-info ...": This is the custom CSS class that styles the testimonial info.
  • style="display: none;": This is the CSS that hides the testimonial info initially.

Classes are removed for brevity, but I’ll keep those classes relevant to the tutorial.

<!-- Dynamically render name and role -->
{
  testimonials.map((testimonial, index) => (
    <div
      class="testimonial-info ..."
      data-index={index + 1}
      style="display: none;">
      <h2>{testimonial.name}</h2>
      <a href="#">{testimonial.title}</a>
    </div>
  ))
}

The full structure

<div class="mt-6 border-t pt-12 max-w-xl mx-auto w-full">
  <!-- Dynamically render testimonials -->
  {
    testimonials.map((testimonial, index) => (
      <div
        class="testimonial pb-6 text-neutral-500 font-medium mx-auto   max-w-2xl h-32 text-balance items-center text-center"
        data-index={index + 1}
        style="display: none;">
        <p>{testimonial.content}</p>
      </div>
    ))
  }

  <div class="flex items-center justify-center mt-12">
    <!-- Buttons to change the active testimonial -->
    {
      testimonials.map((testimonial, index) => (
        <button
          class="avatar-button inline-block mx-2 font-bold text-center rounded-full  focus:outline-none focus:ring-2 ring-offset-4  ring-offset-white ring-orange-600 size-12"
          data-index={index + 1}>
          <img
            class="inline-block size-12 rounded-full object-cover"
            src={testimonial.imageUrl}
            alt="#_"
          />
        </button>
      ))
    }
  </div>
  <!-- Dynamically render name and role -->
  {
    testimonials.map((testimonial, index) => (
      <div
        class="testimonial-info text-center py-6"
        data-index={index + 1}
        style="display: none;">
        <h2 class="text-black  font-medium text-base">{testimonial.name}</h2>
        <a
          href="#"
          class="text-xs text-orange-500">
          {testimonial.title}
        </a>
      </div>
    ))
  }
</div>

The script

  • document.addEventListener("DOMContentLoaded", () => {: This is the event listener that will be added to the document object.
  • const testimonials = document.querySelectorAll(".testimonial");: This is the code that will select all the testimonial elements.
  • const testimonialInfos = document.querySelectorAll(".testimonial-info");: This is the code that will select all the testimonial info elements.
  • const avatarButtons = document.querySelectorAll(".avatar-button");: This is the code that will select all the avatar buttons.
  • const showTestimonial = (index) => {: This is the function that will be called when a button is clicked.
  • testimonials.forEach((testimonial) => {: This is the code that will iterate over each testimonial element.
  • `testimonial.style.display = testimonial.dataset.index == index ?
  • testimonial.dataset.index == index ? "block" : "none";: This is the code that will change the display property of the testimonial element based on the index of the button that was clicked.
  • testimonialInfos.forEach((info) => {: This is the code that will iterate over each testimonial info element.
  • info.style.display = info.dataset.index == index ? "block" : "none";: This is the code that will change the display property of the testimonial info element based on the index of the button that was clicked.
  • avatarButtons.forEach((button) => {: This is the code that will iterate over each avatar button element.
  • button.addEventListener("click", (e) => {: This is the event listener that will be added to each avatar button element.
  • e.preventDefault();: This is the code that will prevent the default behavior of the button, which is to navigate to the link.
  • const index = button.dataset.index;: This is the code that will get the index of the button that was clicked.
  • showTestimonial(index);: This is the code that will call the showTestimonial function with the index of the button that was clicked.
  • showTestimonial(1);: This is the code that will call the showTestimonial function with the index of the first testimonial.
document.addEventListener("DOMContentLoaded", () => {
    const testimonials = document.querySelectorAll(".testimonial");
    const testimonialInfos = document.querySelectorAll(".testimonial-info");
    const avatarButtons = document.querySelectorAll(".avatar-button");

    const showTestimonial = (index) => {
        testimonials.forEach((testimonial) => {
            testimonial.style.display =
                testimonial.dataset.index == index ? "block" : "none";
        });
        testimonialInfos.forEach((info) => {
            info.style.display = info.dataset.index == index ? "block" : "none";
        });
    };

    avatarButtons.forEach((button) => {
        button.addEventListener("click", (e) => {
            e.preventDefault();
            const index = button.dataset.index;
            showTestimonial(index);
        });
    });
    showTestimonial(1);
});

Conclusion

This is a simple testimonial section that can be used for any type of content, such as products, services, or blog posts or anything else where you need to prove your value. But before using it make sure to make it fully accessible.

Hope you enjoyed this tutorial and have a great day!

/Michael Andreuzza

Did you like this tutorial? Please share it with your friends!

Own all themes forever for $199.

Includes new themes, updates, unlimited projects, and lifetime support. — No subscription required!