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 devOpen the dev dashboard to preview and test every widget from one page:
http://localhost:5173/dev.htmlThe 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-handleThe panel app should be running on http://localhost:3000.
Dynamic HTML example:
npm run buildhttp://localhost:5173/examples/dynamic-embed.html?shop=your-shop.myshopify.com&handle=product-handleThis 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-idfor the product reviews and product card widgets; this is treated as the Shopify product IDdata-shopdata-product-handledata-shopify-product-iddata-panel-product-iddata-hide-empty="true"for the product card widget if zero-review products should render nothing; this is the defaultdata-hide-empty="false"ordata-show-empty="true"for the product card widget if zero-review products should still renderdata-page-sizefor the store reviews list and product reviews widgetsdata-hide-ratingsfor the store reviews list and product reviews widgets, for example1,2data-font-familydata-heading-font-familydata-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-storecache headers. The response depends on the exact product ID query, and shared CDN caching can return another product grid's summaries. - Add
preconnecthints 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.comStore reviews list local example:
http://localhost:5173/examples/dynamic-store-reviews-embed.html?shop=your-shop.myshopify.com&take=6&hideRatings=1,2Product reviews local example:
http://localhost:5173/product-reviews.html?shop=your-shop.myshopify.com&productId=1234567890&take=6Product card local example:
http://localhost:5173/examples/dynamic-product-card-embed.html?shop=your-shop.myshopify.com&productIds=1234567890,9876543210