Reviews carousel
A carousel of customer reviews — the average and review count on top, then up to 12 featured
reviews as cards with stars, date, title, text, author and a "certified review" mark. Search engines
and AI crawlers read every review without executing anything, and the structured data includes the
product rating plus one Review item per card.
See Google's structured data documentation for products and review snippets for the exact fields it looks for.
Read Before you start first. The schema mode you pick
depends on whether your page already publishes its own Product structured data.
How it looks and behaves
- One card per view on narrow containers, three on wide ones. The switch depends on the width of the widget itself (1,000 px or more), not on the viewport.
- With JavaScript (Step 2) the carousel gets arrows and dots — in one row under the card on narrow containers, so the card uses the full width, and beside the cards on wide ones — is circular — the arrows are never disabled, after the last review comes the first — and moves on by itself every 6 seconds. It waits while the pointer is over a card, an arrow or a dot, while a control has keyboard focus, and it never moves by itself for visitors who prefer reduced motion.
- Without JavaScript every review is still in the page and the cards scroll sideways (mouse, finger or keyboard), with a thin scrollbar as the cue. Nothing is hidden or cut from the markup. The room for the arrows and dots stays reserved under the cards, so the page does not move when the script adds them.
- Only featured reviews are shown — up to 12, with the author exactly as
Shopadvizor stores it and the date as
dd/mm/yyyy. A product with no featured reviews yet prints the compact rating instead. - Long reviews do not stretch the cards. The title is cut at 2 lines and the review text at 4, with an ellipsis; the full text stays in the markup and in the structured data. Cards in the same view stretch to the tallest one, so the carousel does not change height from one review to the next.
Request
The only difference from the ratings widget is variant=reviews:
GET https://saz-ssr-server.public.shopadvizor.com/v1/fragment?ean=3175681308237&lang=fr&schema=full&variant=reviews&name=Moelleux%20p%C3%A9pites%20de%20chocolat%20Matin%20Gerbl%C3%A9
Authorization: Basic <your credential>
The response carries the reviews themselves, so it is larger than the compact one: around 50 KB of HTML. What that costs depends on the hop:
- To your visitors it is part of your page, so it travels compressed if your server or CDN already compresses your HTML. Compressed, it is about 7 KB, and it needs no special treatment.
- From the service to your server it travels uncompressed. Rather than fetching it on every page
view, keep it on your side: the response says for how long,
Cache-Control: s-maxage=3600(one hour). Do not keep an empty response that long (see Troubleshooting).
Server-side examples for each stack are in Step 1.
Examples
Each example is the fragment exactly as the endpoint returns it, with real product data, saved when these docs were built. Their JSON-LD is removed, so search engines do not index a documentation page as the product's ratings. Each one shows the request that produced it.
Use Mobile / Desktop above any example to preview it at that width; the choice applies to every example on the page. The desktop preview is the real layout, scaled down to fit this column.
The carousel changes layout on the width of the widget itself, not of the screen:
| Widget width | Layout |
|---|---|
| under 1,000 px | One card per view. Arrows and dots in one row under the card |
| 1,000 px or more | Three cards per view. Arrows beside the cards, dots underneath |
So a carousel in a narrow column of a desktop page shows the mobile layout. Give its container 1,000 px or more if you want three cards.
Default
/v1/fragment?ean=3175681302679&variant=reviews
Star colours
The star parameter picks one colour of the closed
palette:
- gold
- blue
- orange
- white
- black
The palette default.
/v1/fragment?ean=3175681302679&variant=reviews&star=gold
Custom CSS
Any colour outside the palette, and the rest of the look, comes from your own stylesheet,
through the --sa-* custom properties on the shopadvizor-rating element. Try them here: every
control restyles the live widget, and the block underneath is the CSS to paste in your stylesheet —
only what differs from the defaults.
/* Change a control above: the CSS to paste in your stylesheet appears here. */
The full list of properties is under Styling.
Long titles and comments
A product whose reviews have long titles and comments. The title is cut to two lines and the comment to four, each with an ellipsis, so long reviews do not stretch the cards. The cut is visual only: the full text is still in the HTML and in the structured data, so search engines and AI crawlers read all of it.
This is the one example that is not exactly what the endpoint returns: the first card's title and comment are lengthened on purpose (a sample text, not a real review), so the cut is visible at any width. The other cards are the product's real reviews.
/v1/fragment?ean=3175681308237&variant=reviews
Without JavaScript
The same fragment with JavaScript disabled — what a search engine or an AI crawler reads, and what a
visitor sees before hydrate.js loads or if it never does:
Every review is in the page as real text. Without the script the cards scroll sideways (mouse, finger or keyboard) with a thin scrollbar as the cue, and there are no arrows, dots or automatic advance. Overlay-scrollbar systems such as macOS show the scrollbar only while scrolling.
Structured data
The reviews follow the same rule as the rating: with schema=full they are nested in the Product
we emit; with schema=rating-only they are attached to your own node through its @id. Without
productId there is no node to attach them to, so rating-only then emits the bare rating only, with
no Review items:
{ "@context": "https://schema.org",
"@id": "https://example.com/p/123#product",
"aggregateRating": { "@type": "AggregateRating", "ratingValue": 4.17,
"reviewCount": 1727, "bestRating": 5, "worstRating": 1 },
"review": [
{ "@type": "Review",
"author": { "@type": "Person", "name": "MARIE D." },
"reviewRating": { "@type": "Rating", "ratingValue": 5,
"bestRating": 5, "worstRating": 1 },
"name": "…", "reviewBody": "…", "datePublished": "2026-10-07" }
// … one item per card, up to 12
] }
The fragment prints them and marks them up. A second set of Review items for the same cards, in
your own markup, is duplicated structured data.
With schema=none the carousel is printed with no JSON-LD at all, and you write the structured
data yourself. It is only worth it if you already publish markup for these same reviews:
GET /v1/data returns the rating numbers but not the reviews, so there is nothing to nest.
The carousel shows the average like the compact rating: two decimals and the locale's comma
(4,17). The JSON-LD ratingValue is the same number with a dot (4.17).
The numbers in these examples are illustrative: the real ones change as new reviews arrive, so the saved examples above may show slightly different counts.
JavaScript enhancement
hydrate.js is about 15 KB gzipped, the same file as for the ratings widget. Without it you only lose the arrows, the dots, the circular
wrap-around and the automatic advance. Their room is already reserved in the fragment, so nothing
below the widget moves when the script adds them. See Step 2.
Styling
The carousel brings its own defaults, taken from the design, so it does not look like the compact
rating. Your own stylesheet and the star and accent parameters
win over them. The palette is the same as for the ratings
widget. The accent parameter sets both --sa-accent and --sa-primary: to give them different
colours, set them from your stylesheet.
| Property | Carousel default | What it colours |
|---|---|---|
--sa-star | #fd9e02 | The stars (the empty ones are an outline in the same colour) |
--sa-surface | #fff | The card background |
--sa-ink | #1e2939 | The average and the review text |
--sa-muted | #6a7282 | The review count |
--sa-line | #e2e8f0 | The separator under the header (narrow layout only), the inactive dots and the scrollbar track without JavaScript |
--sa-accent | #004d8e | The review title, date, author and author icon, and the focus ring |
--sa-primary | #003481 | The arrows, the active dot and the scrollbar without JavaScript |
--sa-verified | #00a63e | The "certified review" mark |
--sa-radius | 5px | The corner radius; cards use 4× this value |
--sa-font | your page's font | The font: the carousel inherits yours unless you set it |
shopadvizor-rating {
--sa-star: #e31c39; /* star fill */
--sa-surface: #fff; /* card background */
--sa-ink: #181b20; /* average and review text */
--sa-primary: #003481; /* arrows and active dot */
--sa-radius: 4px; /* corner radius (cards use 4×) */
}
The carousel loads no webfont of its own.
Carousel-specific troubleshooting
These come on top of the common troubleshooting.
| What you see | What it means |
|---|---|
| The compact rating appears instead of the carousel | The product has no featured reviews yet, the reviews part of the lookup failed, or variant is misspelled (anything other than reviews prints the compact rating and is logged). It is not an error: print what you get. |
| Fewer than 12 cards | Only featured reviews are shown, up to 12. A product may have fewer. |
| Rich Results Test shows the product but no reviews | The compact rating was printed (see above), the call used schema=none, which prints no JSON-LD, or it used schema=rating-only without productId, which emits the bare rating with no Review items. |
| The carousel has no arrows or dots | hydrate.js is not loaded (Step 2), or all the reviews fit in a single view. |
| The carousel does not move by itself | The automatic advance is part of hydrate.js. It also waits while the pointer is over a card, an arrow or a dot or while a control has keyboard focus, and it never runs for visitors who prefer reduced motion. |