Skip to main content

Reviews carousel

FranceThis guide
SpainComing soon

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.

Before you integrate

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.

Breakpoint: 1,000 px of widget width

The carousel changes layout on the width of the widget itself, not of the screen:

Widget widthLayout
under 1,000 pxOne card per view. Arrows and dots in one row under the card
1,000 px or moreThree 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​

Widget 343 px · one card · breakpoint 1000 px
Example, with hydrate.js
/v1/fragment?ean=3175681302679&variant=reviews

Star colours​

The star parameter picks one colour of the closed palette:

The palette default.

Widget 343 px · one card · breakpoint 1000 px
Example, with hydrate.js
/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.

Widget 343 px · one card · breakpoint 1000 px
Example, with hydrate.js
5px
Your stylesheet
/* 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.

Widget 343 px · one card · breakpoint 1000 px
Example, with hydrate.js
/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:

Widget 343 px · one card · breakpoint 1000 px
Example, JavaScript disabled

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
] }
Do not write the reviews yourself

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.

PropertyCarousel defaultWhat it colours
--sa-star#fd9e02The stars (the empty ones are an outline in the same colour)
--sa-surface#fffThe card background
--sa-ink#1e2939The average and the review text
--sa-muted#6a7282The review count
--sa-line#e2e8f0The separator under the header (narrow layout only), the inactive dots and the scrollbar track without JavaScript
--sa-accent#004d8eThe review title, date, author and author icon, and the focus ring
--sa-primary#003481The arrows, the active dot and the scrollbar without JavaScript
--sa-verified#00a63eThe "certified review" mark
--sa-radius5pxThe corner radius; cards use 4× this value
--sa-fontyour page's fontThe 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.

These come on top of the common troubleshooting.

What you seeWhat it means
The compact rating appears instead of the carouselThe 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 cardsOnly featured reviews are shown, up to 12. A product may have fewer.
Rich Results Test shows the product but no reviewsThe 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 dotshydrate.js is not loaded (Step 2), or all the reviews fit in a single view.
The carousel does not move by itselfThe 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.