← Back to all tutorials

How to create an interactive pricing table with Tailwind CSS and JavaScript

Pricing table with Alpine.js
Published on May 13 2024 by Michael Andreuzza

It’s Monday! Let’s get started with an interactive pricing table. Remember we did this with Tailwind CSS and Alpine.js? If not you can see it here. So let’s do it again but with javaScript. But first of all, an intro.

What is and pricing tabel toggle?

A pricing table toggle is a way to show for example monthly or annual pricing plans. It’s a great way to make it easy for users to switch between plans.

This is the structure of the project: Understanding the code:

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

The buttons

These buttons will be used to change the pricing of any card on the pricing table

  • id="monthlyBtn": This is the id of the button that will change the pricing of the card to monthly.
  • id="annualBtn": This is the id of the button that will change the pricing of the card to annual.
<div>
  <button
    id="monthlyBtn">
    Monthly
  </button>
  <button
    id="annualBtn">
    Annual
  </button>
</div>

The array

To simplify the code, we’ll use an array that store the pricing plans data, you don’t really want to hardcode the data in the HTML. It would be a pain to maintain it. So let’s create an array to store the data. Let’s add it on the same page, so we can access it easily and avoid looking for it.

  • const pricingPlans = [...]: This is the array that will store the pricing plans data.
  • name: "Starter Pack",: This is the name of the plan.
  • link: "#_",: This is the link of the plan.
  • monthlyPrice: "15",: This is the monthly price of the plan.
  • annualPrice: "8",: This is the annual price of the plan.
  • description: "This plan is ideal for individual users...",: This is the description of the plan.
  • cardBgClass: "bg-black/20",: This is the background color of the card.
  • buttonClass: "text-white bg-black/50 hover:bg-black/20",: This is the button color of the card.
  • features: ["5 mb/PDF", "75 pages/PDF"],: This is the features of the plan.
  • unavailableFeatures: ["Gpt-3.5-turbo model"],: This is the unavailable features of the plan.
const pricingPlans = [
  {
    name: "Starter Pack",
    link: "#_",
    monthlyPrice: "15",
    annualPrice: "8",
    description: "This plan is ideal for individual users...",
    cardBgClass: "bg-black/20",
    buttonClass: "text-white bg-black/50 hover:bg-black/20",
    features: ["5 mb/PDF", "75 pages/PDF"],
    unavailableFeatures: ["Gpt-3.5-turbo model"],
  },
  // More plans...
];

The cards

This is the template literall that will be used to render the pricing plans according to the data in the array.

  • pricingPlans.map((plan) => (: This is the array that will be used to iterate over the pricing plans.
  • data-monthly-price={plan.monthlyPrice}: This is the data attribute that will be used to store the monthly price of the plan.
  • data-annual-price={plan.annualPrice}: This is the data attribute that will be used to store the annual price of the plan.
  • class={plan.cardBgClass}: This is the class that will be used to style the card. Is a variable that will be used to store the background color of the card. We need one of the cards to be “popular” so we can style it differently.
  • {plan.name}: This is the name of the plan.
  • <span>${plan.monthlyPrice}</span>: This is the span that will be used to display the monthly price of the plan.
  • <span>${plan.annualPrice}</span>: This is the span that will be used to display the annual price of the plan.
  • {plan.description}: This is the description of the plan.
  • {plan.features.map((feature) => (: This is the array that will be used to iterate over the available features of the plan.
  • {plan.unavailableFeatures.map((feature) => (: This is the array that will be used to iterate over the unavailable features of the plan.
  • class={${plan.buttonClass}}>: This is the class that will be used to style the button
  • href={plan.link}: This is the href that will be used to link to the plan.
{
  pricingPlans.map((plan) => (
    <div
      data-monthly-price={plan.monthlyPrice}
      data-annual-price={plan.annualPrice}
      class={`${plan.cardBgClass}`}>
      <div>
        <div >
          <p>
            {plan.name}
          </p>
          <p>
            <span>
              <span >${plan.monthlyPrice}</span>
              <span >${plan.annualPrice}</span>
            </span>
            <span>
              /m
              <span
                style="display: none;">
                (billed annually)
              </span>
            </span>
          </p>
        </div>
        <p>{plan.description}</p>
        <ul>
          {plan.features.map((feature) => (
            <li>
              <svg>
                <!-- Icons for available features goes here -->
              </svg>
              {feature}
            </li>
          ))}
          {plan.unavailableFeatures.map((feature) => (
            <li>
              <svg>
                <!-- Icons for NON available features goes here -->
              </svg>
              {feature}
            </li>
          ))}
        </ul>
      </div>
      <div>
        <a
        href={plan.link}
          class={`${plan.buttonClass}`}>
          Get started
        </a>
      </div>
    </div>
  ))
}

The script

This is the script that will be used to update the pricing plans according to the user’s choice.

  • document.addEventListener("DOMContentLoaded", function () {: This is the event listener that will be used to run the code when the DOM is loaded.
  • const monthlyBtn = document.getElementById("monthlyBtn");: This is the code that will be used to get the monthly button element.
  • const annualBtn = document.getElementById("annualBtn");: This is the code that will be used to get the annual button element.
  • monthlyBtn.addEventListener("click", toggleBillingCycle);: This is the code that will be used to add an event listener to the monthly button.
  • annualBtn.addEventListener("click", toggleBillingCycle);: This is the code that will be used to add an event listener to the annual button.
  • function toggleBillingCycle() {: This is the code that will be used to define the toggleBillingCycle function.
  • const isAnnual = this.id === "annualBtn";: This is the code that will be used to check if the button that was clicked is the annual button.
  • const activeBtn = isAnnual ? annualBtn : monthlyBtn;: This is the code that will be used to get the active button.
  • const inactiveBtn = isAnnual ? monthlyBtn : annualBtn;: This is the code that will be used to get the inactive button.
  • activeBtn.classList.add("bg-black/50", "text-white");: This is the code that will be used to add the active button class.
  • inactiveBtn.classList.remove("bg-black/50", "text-black");: This is the code that will be used to remove the inactive button class.
  • updatePricingDisplay(isAnnual);: This is the code that will be used to update the pricing display.
 document.addEventListener("DOMContentLoaded", function () {
    const monthlyBtn = document.getElementById("monthlyBtn");
    const annualBtn = document.getElementById("annualBtn");

    monthlyBtn.addEventListener("click", toggleBillingCycle);
    annualBtn.addEventListener("click", toggleBillingCycle);

    function toggleBillingCycle() {
      const isAnnual = this.id === "annualBtn";
      const activeBtn = isAnnual ? annualBtn : monthlyBtn;
      const inactiveBtn = isAnnual ? monthlyBtn : annualBtn;

      activeBtn.classList.add("bg-black/50", "text-white");
      inactiveBtn.classList.remove("bg-black/50", "text-black");

      updatePricingDisplay(isAnnual);
    }

    function updatePricingDisplay(isAnnual) {
      const plans = document.querySelectorAll(".pricing-card");

      plans.forEach((plan) => {
        const priceElement = plan.querySelector(".actualPrice");
        const billingCycleElement = plan.querySelector(".actualCycle");

        const monthlyPrice = plan.dataset.monthlyPrice;
        const annualPrice = plan.dataset.annualPrice;

        priceElement.textContent = isAnnual ? annualPrice : monthlyPrice;
        billingCycleElement.textContent = isAnnual ? "(monthly billed annually)" : "/m";
      });
    }

    updatePricingDisplay(false);
});

Conclusion

In this tutorial, we learned how to create an interactive pricing table with Tailwind CSS and JavaScript. We created a pricing table that allows users to switch between monthly and annual pricing plans. We also added a link to each plan and used JavaScript to update the pricing display when the user clicks on the monthly or annual button. This tutorial is a great starting point for anyone looking to create an interactive pricing table with Tailwind CSS and JavaScript. Do not forget to make it responsive and fully accessible.

Hope you enjoyed this tutorial and learned something new.

/Michael Andreuzza

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

Reviews and opinions

  • "I bought a beautiful theme from Lexington a couple weeks ago. I didn't know Astro at the time, but Michael helped me get set up and really went above and beyond with his support. Now I'm happily redoing my site to look gorgeous with his template."

    Stuart

    Stuart

    Creator of saasydb.com

  • "Michael is one of the best designers on Twitter, would highly recommend his Lexington Themes if you want something in tailwind that doesn’t look the same as everyone else!"

    Alex Hughes

    Alex Hughes

    letsloopin.com

Get lifetime access to every theme available today for $199 and own them forever. Plus, new themes, lifetime updates, use on unlimited projects and enjoy lifetime support.

No subscription required!

Lexington

Beautifully designed HTML, Astro.js and Tailwind themes! Save months of time and build your startup landing page in minutes.