Star ratings on the collection grid are pure social proof, and they're a small amount of Liquid once you know where the rating lives. Here's the finished feature: two products show their stars and review count, and the newly listed decaf (no reviews yet) shows nothing.
The theme reads reviews, it doesn't store them
A review app (Judge.me, Loox, Yotpo, Shopify Product Reviews) owns the reviews and writes an aggregate to the reviews.rating and reviews.rating_count metafields. You read those. Never build review storage in the theme.
Step 1
Read the rating and draw the stars
Inside your card loop, round the rating value and loop one to five, printing a filled star when the index is within the rating:
<span class="rating-stars"> {% assign rounded = product.metafields.reviews.rating.value | round %} {% for i in (1..5) %} {% if i <= rounded %}★{% else %}<span class="star--empty">☆</span>{% endif %} {% endfor %}</span>Step 2
Add the count and an accessible label
<span class="rating-stars" role="img" aria-label="Rated {{ product.metafields.reviews.rating.value }} out of 5"> {% assign rounded = product.metafields.reviews.rating.value | round %} {% for i in (1..5) %}{% if i <= rounded %}★{% else %}<span class="star--empty">☆</span>{% endif %}{% endfor %}</span><span class="rating-count">({{ product.metafields.reviews.rating_count }})</span>Step 3
Hide it for products with no reviews
{% if product.metafields.reviews.rating_count > 0 %} {% comment %} ...the stars + count from above... {% endcomment %}{% endif %}Tip
There's an interactive version where you write each step yourself and watch the stars appear on a real collection. Build it in the editor →