E-commerce & Retail

Free Star Rating Badge Widget

Show your aggregate rating with stars, a score, and a review count.

A compact badge that displays your average rating as partially filled SVG stars alongside a numeric score and review count. Perfect for stores, service businesses, and SaaS sites that want social proof near a buy button or footer. Paste one script tag, set your rating, and it renders — with an optional link to your reviews page.

Live demo

Embed this widget

Paste either snippet into your page — a Custom HTML block on WordPress, Shopify, or Squarespace works too. Both snippets update live as you customize, so what you copy is exactly what you configured.

Hosted (recommended)html
<script src="https://hayneslab.dev/widgets/star-rating-badge.js" data-hl-rating="4.8" data-hl-count="231" data-hl-link="https://hayneslab.dev/contact" async></script>
<!-- Free Star Rating Badge widget by haynes lab — https://hayneslab.dev/tools/widgets/star-rating-badge -->
Inline (self-contained)html
<!-- Free Star Rating Badge widget by haynes lab — https://hayneslab.dev/tools/widgets/star-rating-badge -->
<script data-hl-rating="4.8" data-hl-count="231" data-hl-link="https://hayneslab.dev/contact">
/*!
 * haynes lab — Star Rating Badge widget (free)
 * Docs & embed code: https://hayneslab.dev/tools/widgets/star-rating-badge
 */
(function () {
  'use strict';

  var PREFIX = 'hlw-star-rating-badge';
  var STYLE_ID = 'hlw-star-rating-badge-styles';
  var STAR_PATH = 'M12 2.6l2.8 6.2 6.7.7-5 4.5 1.4 6.6-5.9-3.4-5.9 3.4 1.4-6.6-5-4.5 6.7-.7z';

  function findScript() {
    return document.currentScript ||
      document.querySelector('script[src*="widgets/star-rating-badge.js"]');
  }

  function injectStyles() {
    if (document.getElementById(STYLE_ID)) return;
    var css =
      '.' + PREFIX + ', .' + PREFIX + ' * { box-sizing: border-box; }' +
      '.' + PREFIX + ' { display: inline-block; position: relative;' +
      ' font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;' +
      ' font-size: 14px; line-height: 1.4; color: #1f2937; }' +
      '.' + PREFIX + '__badge { display: inline-flex; align-items: center; gap: 8px;' +
      ' padding: 8px 14px; background: #ffffff; border: 1px solid #e5e7eb; border-radius: 999px;' +
      ' color: inherit; text-decoration: none; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); }' +
      'a.' + PREFIX + '__badge:hover { border-color: #d1d5db; }' +
      'a.' + PREFIX + '__badge:focus-visible { outline: 2px solid var(--hlw-accent); outline-offset: 2px; }' +
      '.' + PREFIX + '__stars { display: inline-flex; gap: 2px; }' +
      '.' + PREFIX + '__star { position: relative; width: 18px; height: 18px; flex: 0 0 auto; }' +
      '.' + PREFIX + '__star svg { display: block; width: 18px; height: 18px; }' +
      '.' + PREFIX + '__star-fill { position: absolute; top: 0; left: 0; height: 100%; overflow: hidden; }' +
      '.' + PREFIX + '__score { font-weight: 700; font-size: 15px; color: #111827; }' +
      '.' + PREFIX + '__count { font-size: 13px; color: #6b7280; white-space: nowrap; }' +
      '.' + PREFIX + '__credit { display: block; margin-top: 4px; text-align: right; font-size: 11px;' +
      ' color: #9ca3af; text-decoration: none; }' +
      '.' + PREFIX + '__credit svg { display: inline-block; vertical-align: -2px; margin-right: 4px; }' +
      '.' + PREFIX + '__credit:hover { color: #6b7280; text-decoration: underline; }';
    var style = document.createElement('style');
    style.id = STYLE_ID;
    style.textContent = css;
    document.head.appendChild(style);
  }

  function el(tag, className, text) {
    var node = document.createElement(tag);
    if (className) node.className = className;
    if (text != null) node.textContent = text;
    return node;
  }

  function starSvg(fill) {
    var svgNS = 'http://www.w3.org/2000/svg';
    var svg = document.createElementNS(svgNS, 'svg');
    svg.setAttribute('viewBox', '0 0 24 24');
    svg.setAttribute('aria-hidden', 'true');
    svg.setAttribute('focusable', 'false');
    var path = document.createElementNS(svgNS, 'path');
    path.setAttribute('d', STAR_PATH);
    path.setAttribute('fill', fill);
    svg.appendChild(path);
    return svg;
  }

  function logoSvg() {
    var svgNS = 'http://www.w3.org/2000/svg';
    var svg = document.createElementNS(svgNS, 'svg');
    svg.setAttribute('viewBox', '0 0 32 32');
    svg.setAttribute('width', '13');
    svg.setAttribute('height', '13');
    svg.setAttribute('aria-hidden', 'true');
    svg.setAttribute('focusable', 'false');
    function circle(cx, cy, r, attrs) {
      var c = document.createElementNS(svgNS, 'circle');
      c.setAttribute('cx', cx);
      c.setAttribute('cy', cy);
      c.setAttribute('r', r);
      for (var key in attrs) c.setAttribute(key, attrs[key]);
      svg.appendChild(c);
    }
    circle(16, 16, 16, { fill: '#0f0a0a' });
    circle(16, 16, 12, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.3 });
    circle(16, 16, 9, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.5 });
    circle(16, 16, 6, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1.2, opacity: 0.9 });
    circle(16, 16, 3, { fill: '#06b6d4' });
    circle(24.5, 7.5, 2, { fill: '#f97316' });
    circle(7, 16, 1.8, { fill: '#f97316' });
    circle(20, 20, 1.5, { fill: '#f97316' });
    return svg;
  }

  function build(script) {
    var rating = parseFloat(script.getAttribute('data-hl-rating'));
    if (isNaN(rating)) {
      console.warn('[haynes lab star-rating-badge] Missing or invalid required data-hl-rating attribute (a number from 0 to 5). Nothing rendered.');
      return;
    }
    rating = Math.max(0, Math.min(5, rating));
    var score = Math.round(rating * 10) / 10;

    var countRaw = script.getAttribute('data-hl-count');
    var count = null;
    if (countRaw != null && countRaw !== '') {
      count = parseInt(countRaw, 10);
      if (isNaN(count) || count < 0) {
        console.warn('[haynes lab star-rating-badge] data-hl-count is not a valid non-negative number. Review count hidden.');
        count = null;
      }
    }

    var link = script.getAttribute('data-hl-link');
    var accent = script.getAttribute('data-hl-accent') || '#f59e0b';

    var summary = 'Rated ' + score + ' out of 5' +
      (count != null ? ' based on ' + count + (count === 1 ? ' review' : ' reviews') : '');

    injectStyles();

    var root = el('span', PREFIX);
    root.style.setProperty('--hlw-accent', accent);

    var badge = el(link ? 'a' : 'span', PREFIX + '__badge');
    if (link) {
      badge.href = link;
      badge.target = '_blank';
      badge.rel = 'noopener';
      badge.setAttribute('aria-label', summary + ' — read reviews');
    }

    var stars = el('span', PREFIX + '__stars');
    stars.setAttribute('role', 'img');
    stars.setAttribute('aria-label', summary);
    for (var i = 0; i < 5; i++) {
      var star = el('span', PREFIX + '__star');
      star.appendChild(starSvg('#e5e7eb'));
      var pct = Math.max(0, Math.min(1, rating - i)) * 100;
      if (pct > 0) {
        var fill = el('span', PREFIX + '__star-fill');
        fill.style.width = pct + '%';
        fill.appendChild(starSvg(accent));
        star.appendChild(fill);
      }
      stars.appendChild(star);
    }
    badge.appendChild(stars);
    badge.appendChild(el('span', PREFIX + '__score', score.toFixed(1)));
    if (count != null) {
      badge.appendChild(el('span', PREFIX + '__count',
        '(' + count + (count === 1 ? ' review' : ' reviews') + ')'));
    }
    root.appendChild(badge);

    var credit = el('a', PREFIX + '__credit');
    credit.appendChild(logoSvg());
    credit.appendChild(document.createTextNode('Powered by haynes lab'));
    credit.href = 'https://hayneslab.dev/tools/widgets/star-rating-badge';
    credit.target = '_blank';
    credit.rel = 'noopener';
    root.appendChild(credit);

    if (script.parentNode && script.parentNode.nodeName !== 'HEAD') {
      script.insertAdjacentElement('afterend', root);
    } else {
      document.body.appendChild(root);
    }
  }

  function init() {
    var script = findScript();
    if (!script) {
      console.warn('[haynes lab star-rating-badge] Could not locate its own script tag. Nothing rendered.');
      return;
    }
    build(script);
  }

  if (document.body) {
    init();
  } else {
    document.addEventListener('DOMContentLoaded', init);
  }
})();

</script>

Configuration

Every option is an attribute on the script tag — the same attributes the customizer above exposes. All attributes are optional unless marked required — defaults apply when omitted.

AttributeDefaultDescription
data-hl-rating(required)Average rating from 0 to 5. Decimals are supported and partial stars are filled accordingly.
data-hl-count(none)Number of reviews shown next to the score. Omit to hide the count.
data-hl-link(none)Optional URL (e.g. your Google reviews page) the whole badge links to. Omit for a static badge.
data-hl-accent"#f59e0b"Any CSS color used for the filled portion of the stars.

Frequently asked questions

How do I install the star rating badge?

Copy the embed snippet and paste it where you want the badge to appear on your page. The widget renders inline right after the script tag — near a buy button, in a product description, or in your footer.

How do I customize the stars and link?

Everything is set through data-hl-* attributes on the script tag. Set data-hl-rating (decimals like 4.8 fill stars partially), data-hl-count for the review total, data-hl-accent for the star color, and data-hl-link to make the badge clickable.

Does it work on WordPress, Shopify, or Squarespace?

Yes. The widget is a single dependency-free vanilla JS file, so it runs anywhere you can paste a script tag — WordPress, Shopify (theme.liquid or a custom liquid block), Squarespace code injection, Webflow, or plain HTML.

More free widgets

Need a custom widget?

We build custom widgets, integrations, and whole products. Tell us what your site needs and we'll scope it.

Get in Touch