Tutorials / / 4 min read
How to create a rating system with Tailwind CSS: JavaScript and Alpine.js
Build a five-star rating system with Tailwind CSS, two ways: vanilla JavaScript or Alpine.js. Click to rate, click again to clear, with a live label.
It’s Friday! And today we are creating a five-star rating system. We’ll build it twice, first with vanilla JavaScript, then with Alpine.js, so you can pick whichever fits your stack.
What is a rating system?
A rating system lets users score content, usually with a row of stars, and helps other people make informed decisions about what they are viewing. Ours has five stars: clicking one sets the rating, clicking the same one again clears it, and a label spells out the current score.
Use cases
- E-commerce: let shoppers rate products so others can buy with confidence.
- Movies and books: collect scores on how much people enjoyed a title.
- Services: gather feedback on support, deliveries, or bookings.
- Content sites: surface the most appreciated articles or recipes.
The markup
id="ratingApp": the container that holds the rating system.id="starsContainer": the container that holds the stars.id="currentRating": the element that displays the current rating.
<div id="ratingApp">
<div id="starsContainer">
<svg
class="... text-zinc-500"
xmlns="http://www.w3.org/2000/svg"
stroke="currentColor"
></svg>
</div>
<div class="mt-2">
<span id="currentRating">0 stars</span>
</div>
</div>The script
document.addEventListener("DOMContentLoaded", function () {: runs the code once the page is loaded.const stars = document.querySelectorAll("#starsContainer svg");andconst currentRating = document.getElementById("currentRating");: select the stars and the rating label.let rating = -1;: stores the current rating,-1means nothing is selected.const updateRatingDisplay = () => {: writes “0 stars” or the star count into the label, then togglestext-orange-500on the stars up to the rating andtext-zinc-500on the rest.star.addEventListener("click", () => {: sets the rating to the clicked star, or clears it back to-1when you click the same star twice, then refreshes the display.
document.addEventListener("DOMContentLoaded", function () {
const stars = document.querySelectorAll("#starsContainer svg");
const currentRating = document.getElementById("currentRating");
let rating = -1;
const updateRatingDisplay = () => {
currentRating.textContent =
rating === -1 ? "0 stars" : rating + 1 + " stars";
stars.forEach((star, index) => {
star.classList.toggle("text-orange-500", index <= rating);
star.classList.toggle("text-zinc-500", index > rating);
});
};
stars.forEach((star, index) => {
star.addEventListener("click", () => {
rating = rating === index ? -1 : index;
updateRatingDisplay();
});
});
updateRatingDisplay();
});The Alpine.js version
Same component, no separate script: the state lives in x-data.
x-data="{ rating: -1 }": stores the rating.<template x-for="(star, index) in [0, 1, 2, 3, 4]" :key="index">: loops out the five stars.@click="rating = (rating === index) ? -1 : index": sets the rating when a star is clicked, or clears it when the same star is clicked again.:class="{ 'text-orange-500': index <= rating, 'text-zinc-500': index > rating }": colors the stars up to the current rating.<span x-text="(rating === -1) ? '0 stars' : rating + 1 + ' stars'">: displays the current rating.
Classes are removed for brevity, but I’ll keep those classes relevant to the tutorial.
<div x-data="{ rating: -1 }">
<!-- Display Stars -->
<div>
<template x-for="(star, index) in [0, 1, 2, 3, 4]" :key="index">
<svg
@click="rating = (rating === index) ? -1 : index"
:class="{ 'text-orange-500': index <= rating, 'text-zinc-500': index > rating }"
>
<!-- SVG Path goes here-->
</svg>
</template>
</div>
<!-- Display Current Rating -->
<div class="mt-2">
<span
x-text="(rating === -1) ? '0 stars' : rating + 1 + ' stars'"
class="text-zinc-500"
></span>
</div>
</div>You can try the Alpine version separately: live demo and source code.
Which one should you use?
Alpine’s x-for renders the stars and keeps the coloring declarative, which is nice when it’s already in your stack. The vanilla version does the same with a small loop and works without any dependency.
Conclusion
Five stars, one piece of state, and a couple of class toggles: that’s the whole rating system. It works for movies, books, products, or anything else your users might want to score.
Hope you enjoyed this tutorial and have a great day!
/Michael Andreuzza