npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

agile-alpaca-shopify-reviews-widget

v0.0.17

Published

React + Vite + TypeScript widget for the public review summary API from `panel`.

Downloads

368

Readme

Shopify Reviews Widget

React + Vite + TypeScript widget for the public review summary API from panel.

Local run

cd widgets
npm install
npm run dev

Open the dev dashboard to preview and test every widget from one page:

http://localhost:5173/dev.html

The dashboard includes a mock API mode for product summary, store summary, store reviews, and product card ratings.

Open the widget with API parameters:

http://localhost:5173/?shop=your-shop.myshopify.com&handle=product-handle

The panel app should be running on http://localhost:3000.

Dynamic HTML example:

npm run build
http://localhost:5173/examples/dynamic-embed.html?shop=your-shop.myshopify.com&handle=product-handle

This page loads the built widget script from dist/shopify-reviews-widget.js; widget styles are injected into Shadow DOM.

Embed

Product summary widget:

<div
  data-shopify-reviews-widget
  data-api-url="https://panel.example.com"
  data-shop="your-shop.myshopify.com"
  data-product-handle="product-handle"
></div>
<script type="module" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/shopify-reviews-widget.js"></script>

Product card rating widget:

<div
  data-shopify-reviews-product-card-widget
  data-product-id="{{ product.id }}"
  data-hide-empty="true"
></div>

<script
  type="module"
  src="https://cdn.jsdelivr.net/npm/[email protected]/dist/shopify-reviews-product-card-widget.js"
  data-api-url="https://panel.example.com"
  data-shop="your-shop.myshopify.com"
  data-body-font-family='Arial, "Segoe UI", sans-serif'
></script>

Store summary widget:

<div
  data-shopify-reviews-store-widget
  data-api-url="https://panel.example.com"
  data-shop="your-shop.myshopify.com"
></div>
<script type="module" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/shopify-reviews-store-widget.js"></script>

Store reviews list widget:

<div
  data-shopify-reviews-store-reviews-widget
  data-api-url="https://panel.example.com"
  data-shop="your-shop.myshopify.com"
  data-page-size="6"
  data-hide-ratings="1,2"
></div>
<script type="module" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/shopify-reviews-store-reviews-widget.js"></script>

Product reviews widget:

<div
  data-shopify-reviews-product-reviews-widget
  data-api-url="https://panel.example.com"
  data-shop="your-shop.myshopify.com"
  data-product-id="{{ product.id }}"
  data-page-size="6"
  data-hide-ratings="1,2"
></div>
<script type="module" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/shopify-reviews-product-reviews-widget.js"></script>

Typography can be configured on any widget:

<div
  data-shopify-reviews-store-reviews-widget
  data-api-url="https://panel.example.com"
  data-shop="your-shop.myshopify.com"
  data-font-family='Inter, "Segoe UI", sans-serif'
  data-heading-font-family='Georgia, "Times New Roman", serif'
  data-body-font-family='Arial, sans-serif'
></div>

The same values can be passed as query params: fontFamily, headingFontFamily, and bodyFontFamily. For CSS-only customization, set --shopify-reviews-font-family, --shopify-reviews-heading-font-family, or --shopify-reviews-body-font-family on the widget host element. The product card rating widget has no separate heading typography; data-body-font-family controls all text in that widget, including the review count and popover rows.

Supported filters:

  • data-product-id for the product reviews and product card widgets; this is treated as the Shopify product ID
  • data-shop
  • data-product-handle
  • data-shopify-product-id
  • data-panel-product-id
  • data-hide-empty="true" for the product card widget if zero-review products should render nothing; this is the default
  • data-hide-empty="false" or data-show-empty="true" for the product card widget if zero-review products should still render
  • data-page-size for the store reviews list and product reviews widgets
  • data-hide-ratings for the store reviews list and product reviews widgets, for example 1,2
  • data-font-family
  • data-heading-font-family
  • data-body-font-family

For product grids, include the product card widget script once per page. It batches all card widgets with data-shopify-reviews-product-card-widget into requests to /api/widgets/product-card-summaries, then renders each card from the shared response. Call window.ShopifyReviewsProductCardWidget.refresh() after dynamically adding more product cards.

Optimized Product Card Install

Use this setup for collection grids, search results, recommendations, and any page with many product cards.

1. Add the mount point inside the product card snippet

Put the mount point where the rating should appear, for example in snippets/card-product.liquid, snippets/product-card.liquid, or the theme's equivalent card snippet.

{% assign reviews_product = card_product | default: product %}

{% if reviews_product %}
  <div
    data-shopify-reviews-product-card-widget
    data-product-id="{{ reviews_product.id }}"
    data-hide-empty="true"
  ></div>
{% endif %}

data-product-id should be the Shopify numeric product ID. The widget also supports data-shopify-product-id, data-panel-product-id, and data-product-handle, but the numeric Shopify ID is the preferred option for product grids.

2. Include the script once per page

Do not put the script tag inside the product card loop. Add it once in layout/theme.liquid, ideally near the end of <body>, or in a section that is rendered only once on pages with product grids.

<script
  type="module"
  src="https://cdn.jsdelivr.net/npm/[email protected]/dist/shopify-reviews-product-card-widget.js"
  data-api-url="https://panel.example.com"
  data-shop="{{ shop.permanent_domain }}"
  data-body-font-family='Arial, "Segoe UI", sans-serif'
></script>

type="module" is deferred by default, so it does not block initial HTML parsing. If the script is included before the product cards exist, it waits for DOMContentLoaded; if it is included after the grid, it starts immediately.

3. Keep empty products invisible

data-hide-empty="true" is the default and is recommended for product grids. Products with no published reviews render nothing, so the card layout stays clean and the page avoids unnecessary zero-state UI.

Use this only when you intentionally want to show an empty rating state:

<div
  data-shopify-reviews-product-card-widget
  data-product-id="{{ reviews_product.id }}"
  data-hide-empty="false"
></div>

4. Refresh after AJAX-loaded cards

If the theme replaces or appends product cards after filtering, pagination, infinite scroll, quick search, or recommendations loading, call refresh() after the new HTML is inserted.

<script>
  document.addEventListener("products:loaded", function () {
    window.ShopifyReviewsProductCardWidget?.refresh();
  });
</script>

Use the real event or callback from the theme/app that updates the grid. The widget marks initialized nodes, so calling refresh() again will only mount newly added cards.

If the theme has no useful callback, use a scoped and debounced observer on the product grid only:

<script>
  var grid = document.querySelector("[data-product-grid]");
  var timer;

  if (grid) {
    new MutationObserver(function () {
      window.clearTimeout(timer);
      timer = window.setTimeout(function () {
        window.ShopifyReviewsProductCardWidget?.refresh();
      }, 80);
    }).observe(grid, { childList: true, subtree: true });
  }
</script>

5. Performance checklist

  • Include one product card widget script per page, never one script per product.
  • Prefer data-product-id="{{ reviews_product.id }}" over handle lookup for grids, using the product object from the card snippet.
  • Keep data-hide-empty="true" unless the design requires zero-review UI.
  • Do not override the product card summaries endpoint's no-store cache headers. The response depends on the exact product ID query, and shared CDN caching can return another product grid's summaries.
  • Add preconnect hints if the widget CDN or panel API is on a different origin and product cards are visible above the fold.
  • Add a server-side cache only if its key includes the normalized shop and full normalized product target list.

Optional preconnect example:

<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
<link rel="preconnect" href="https://panel.example.com" crossorigin>

Store summary local example:

http://localhost:5173/examples/dynamic-store-embed.html?shop=your-shop.myshopify.com

Store reviews list local example:

http://localhost:5173/examples/dynamic-store-reviews-embed.html?shop=your-shop.myshopify.com&take=6&hideRatings=1,2

Product reviews local example:

http://localhost:5173/product-reviews.html?shop=your-shop.myshopify.com&productId=1234567890&take=6

Product card local example:

http://localhost:5173/examples/dynamic-product-card-embed.html?shop=your-shop.myshopify.com&productIds=1234567890,9876543210