# Reviews summary: free Shopify section

> Star rating and review count from your review app's reviews.rating metafields, linked to the reviews. Shows nothing when a product has no rating.

Reviews summary is a free Shopify section that shows a product's average star rating and review count from the standard reviews.rating and reviews.rating_count metafields that most review apps fill in, with a link down to the reviews. A review app's own block can also be placed inside it. When a product has no rating it renders nothing for shoppers, so it never shows sample or made-up ratings. It works in Online Store 2.0 themes with Liquid and CSS only.

- **Category:** Social proof and trust
- **Use on:** Product, Landing pages
- **Reads:** Product rating metafields (reviews.rating, reviews.rating_count) or a review app block
- **Size:** 15 KB in 1 file, no JavaScript
- **License:** MIT, © 2026 Lintel
- **Page:** https://lintelagency.com/resources/shopify-sections/reviews-summary
- **Live demo:** https://lintelagency.com/resources/shopify-sections/demo/lib-reviews-summary.html
- **Added:** 2026-11-02

## What it’s for

- Star rating near the top of a product page that jumps to the review app's section
- Rating line on a landing page built around one product, using the product picker
- A home for a review app's block when the theme has no place for it

## Editor settings

In the theme editor the section is called “Lib · Reviews summary”. IDs, types, labels and defaults come from the section’s schema; the labels are what the theme editor shows.

### Content

| ID | Type | Label | Default | Options or range |
| --- | --- | --- | --- | --- |
| `product` | `product` | Product | – |  |
| `heading` | `inline_richtext` | Heading | – |  |
| `heading_tag` | `select` | Heading tag | h2 | `h1`, `h2`, `h3`, `p` |
| `heading_size` | `select` | Heading size | medium | `small`, `medium`, `large` |
| `show_count` | `checkbox` | Show review count | `true` |  |
| `show_max` | `checkbox` | Show the scale (/ 5) | `false` |  |
| `reviews_link` | `text` | Link to the reviews | #reviews |  |
| `link_label` | `text` | Link text | Read reviews |  |

### Words

| ID | Type | Label | Default | Options or range |
| --- | --- | --- | --- | --- |
| `label_rated` | `text` | Screen reader: before the rating | Rated |  |
| `label_out_of` | `text` | Screen reader: between rating and scale | out of |  |
| `label_review` | `text` | Review count word, one | review |  |
| `label_reviews` | `text` | Review count word, more than one | reviews |  |

### Layout

| ID | Type | Label | Default | Options or range |
| --- | --- | --- | --- | --- |
| `style` | `select` | Style | inline | `inline`, `card` |
| `star_size` | `range` | Star size | `20` | 14–40 px, step 2 |
| `section_width` | `select` | Width | page | `page`, `full` |
| `text_alignment` | `text_alignment` | Alignment on desktop | left |  |
| `text_alignment_mobile` | `select` | Alignment on mobile | left | `left`, `center`, `right` |

### Colors

| ID | Type | Label | Default | Options or range |
| --- | --- | --- | --- | --- |
| `color_bg` | `color` | Background | #FFFDF9 |  |
| `color_bg_gradient` | `color_background` | Background gradient | – |  |
| `color_text` | `color` | Text | #1C1917 |  |
| `color_star` | `color` | Stars | #B7791F |  |
| `color_star_empty` | `color` | Empty stars | #D9D2C5 |  |
| `color_card` | `color` | Card background | #F5F2EC |  |

### Spacing and visibility

| ID | Type | Label | Default | Options or range |
| --- | --- | --- | --- | --- |
| `padding_top` | `range` | Top padding on desktop | `24` | 0–200 px, step 4 |
| `padding_bottom` | `range` | Bottom padding on desktop | `24` | 0–200 px, step 4 |
| `padding_top_mobile` | `range` | Top padding on mobile | `16` | 0–160 px, step 4 |
| `padding_bottom_mobile` | `range` | Bottom padding on mobile | `16` | 0–160 px, step 4 |
| `show_on` | `select` | Show on | all | `all`, `desktop`, `mobile` |
| `anchor_id` | `text` | Anchor ID | – |  |

### Motion

| ID | Type | Label | Default | Options or range |
| --- | --- | --- | --- | --- |
| `animate` | `checkbox` | Fill the stars on load | `false` |  |

## Blocks

### @app (`@app`)

No settings.

## Install

### In Shopify admin

1. Work on a copy, not your live theme: in Shopify admin go to **Online Store > Themes**, open the **…** menu on your current theme and choose **Duplicate**.
2. On the copy, open the **…** menu and choose **Edit code**.
3. In the `sections` folder, add a new file named `lib-reviews-summary.liquid` and paste the contents of `sections/lib-reviews-summary.liquid` from the source below. Save.
4. Open **Customize** on the copy, choose **Add section** and pick **Lib · Reviews summary**. Replace the sample content with your own and set the colors.
5. Preview the copy, then publish it when it looks right.

### With Shopify CLI

From your local theme folder, download the files and push only them to an unpublished theme (find its ID with `shopify theme list`):

```sh
curl -fsSL -o sections/lib-reviews-summary.liquid https://lintelagency.com/resources/shopify-sections/files/lib-reviews-summary.liquid
shopify theme push --theme <unpublished-theme-id> --only sections/lib-reviews-summary.liquid
```

Never push to the live theme: always pass the ID of an unpublished theme and never use `--live` or `--allow-live`. Then add the section in the theme editor as above.

## Files

- [sections/lib-reviews-summary.liquid](https://lintelagency.com/resources/shopify-sections/files/lib-reviews-summary.liquid): goes in `sections/`

## Questions

### Where does the rating come from?

From the product metafields reviews.rating (Shopify's rating type, with its scale) and reviews.rating_count. Shopify's own Product Reviews app used them and many review apps still write them; check your app's settings for "sync ratings to metafields" or similar.

### What happens if a product has no reviews?

Shoppers see nothing: the section renders no HTML at all. In the theme editor it shows a short note so you know why it is empty. It never shows placeholder stars or sample numbers.

### Does it add review structured data for Google?

No. Rating markup belongs next to the reviews themselves, and review apps and themes usually output it already; a second copy here could conflict. The section only displays the rating.

### Can I use my review app's widget instead?

Yes. Add the app's block inside the section (Add block, then Apps). It shows with or without the metafield rating, so you can use either or both.

### Is the rating readable by screen readers?

Yes. The stars are decorative; screen readers hear one sentence such as "Rated 4.6 out of 5, 128 reviews", and every word in it is a setting you can translate.

## Source

### sections/lib-reviews-summary.liquid

```liquid
{%- comment -%}
  MIT License · (c) 2026 Lintel · https://lintelagency.com/resources/shopify-sections
{%- endcomment -%}
{%- comment -%}
  Reviews summary: the product's average star rating and review count, read from the standard
  metafields a review app fills in (reviews.rating and reviews.rating_count), with a link to the
  reviews on the page. A review app's own block can sit inside it. Without a rating (and without
  an app block) it renders nothing for shoppers: it never shows sample or made-up ratings.
  On product templates it uses the page's product; elsewhere the product picked in the settings.
  No JavaScript. Depends on: nothing.
{%- endcomment -%}

{%- liquid
  assign s = section.settings
  assign p = s.product
  if p == blank
    assign p = product
  endif
  assign rating = p.metafields.reviews.rating.value
  assign count = p.metafields.reviews.rating_count.value | default: 0
  assign has_rating = false
  if rating != blank and rating.rating != blank
    assign has_rating = true
  endif
  assign heading_tag = s.heading_tag | default: 'h2'
-%}

{%- if has_rating or section.blocks.size > 0 or request.design_mode -%}
  <div
    class="lib-reviews-summary lib-reviews-summary--{{ s.section_width }}"
    {% if s.anchor_id != blank %}
      id="{{ s.anchor_id | handleize }}"
    {% endif %}
    data-show="{{ s.show_on }}"
    data-style="{{ s.style }}"
    {% if s.animate %}
      data-animate
    {% endif %}
    style="
      --lib-bg: {{ s.color_bg }};
      {% if s.color_bg_gradient != blank %}--lib-bg-image: {{ s.color_bg_gradient }};{% endif %}
      --lib-text: {{ s.color_text }};
      --lib-star: {{ s.color_star }};
      --lib-star-empty: {{ s.color_star_empty }};
      --lib-card: {{ s.color_card }};
      --lib-star-size: {{ s.star_size }}px;
      --lib-pt: {{ s.padding_top }}px;
      --lib-pb: {{ s.padding_bottom }}px;
      --lib-pt-m: {{ s.padding_top_mobile }}px;
      --lib-pb-m: {{ s.padding_bottom_mobile }}px;
    "
  >
    <div class="lib-reviews-summary__inner" data-align="{{ s.text_alignment }}" data-align-m="{{ s.text_alignment_mobile }}">
      {%- if s.heading != blank -%}
        <{{ heading_tag }} class="lib-reviews-summary__heading lib-reviews-summary__heading--{{ s.heading_size }}">
          {{- s.heading -}}
        </{{ heading_tag }}>
      {%- endif -%}

      {%- if has_rating -%}
        {%- liquid
          assign value = rating.rating | plus: 0
          assign max = rating.scale_max | plus: 0
          if max <= 0
            assign max = 5
          endif
          assign stars = max | round
          assign fill = value | times: 100.0 | divided_by: max | round: 1
          assign shown = value | round: 1
          assign tag = 'div'
          if s.reviews_link != blank
            assign tag = 'a'
          endif
        -%}
        <{{ tag }}
          class="lib-reviews-summary__summary"
          {% if tag == 'a' %}
            href="{{ s.reviews_link | escape }}"
          {% endif %}
        >
          <span class="lib-reviews-summary__sr">
            {{- s.label_rated | escape }} {{ shown }} {{ s.label_out_of | escape }} {{ max | round: 1 -}}
            {%- if s.show_count %}, {{ count }} {% if count == 1 %}{{ s.label_review | escape }}{% else %}{{ s.label_reviews | escape }}{% endif %}{% endif -%}
            {%- if tag == 'a' and s.link_label != blank %}. {{ s.link_label | escape }}{% endif -%}
          </span>
          <span class="lib-reviews-summary__visual" aria-hidden="true">
            <span class="lib-reviews-summary__stars" style="--lib-fill: {{ fill }}%;">
              {%- capture star -%}<svg viewBox="0 0 24 24" focusable="false"><path d="m12 2.8 2.8 5.8 6.3.9-4.6 4.4 1.1 6.3L12 17.2l-5.6 3 1.1-6.3-4.6-4.4 6.3-.9z"/></svg>{%- endcapture -%}
              <span class="lib-reviews-summary__row lib-reviews-summary__row--empty">{% for i in (1..stars) %}{{ star }}{% endfor %}</span>
              <span class="lib-reviews-summary__row lib-reviews-summary__row--full">{% for i in (1..stars) %}{{ star }}{% endfor %}</span>
            </span>
            <span class="lib-reviews-summary__value">{{ shown }}</span>
            {%- if s.show_max -%}
              <span class="lib-reviews-summary__max">/ {{ max | round: 1 }}</span>
            {%- endif -%}
            {%- if s.show_count -%}
              <span class="lib-reviews-summary__count">
                {{- count }} {% if count == 1 %}{{ s.label_review | escape }}{% else %}{{ s.label_reviews | escape }}{% endif -%}
              </span>
            {%- endif -%}
            {%- if tag == 'a' and s.link_label != blank -%}
              <span class="lib-reviews-summary__link">{{ s.link_label | escape }}</span>
            {%- endif -%}
          </span>
        </{{ tag }}>
      {%- elsif request.design_mode and section.blocks.size == 0 -%}
        <p class="lib-reviews-summary__hint">
          No rating for this product yet. This section shows the reviews.rating and reviews.rating_count
          metafields your review app fills in, or an app block you add here. Shoppers see nothing until then.
        </p>
      {%- endif -%}

      {%- for block in section.blocks -%}
        {%- if block.type == '@app' -%}
          <div class="lib-reviews-summary__app">{% render block %}</div>
        {%- endif -%}
      {%- endfor -%}
    </div>
  </div>
{%- endif -%}

{% stylesheet %}
  .lib-reviews-summary {
    background-color: var(--lib-bg);
    background-image: var(--lib-bg-image, none);
    color: var(--lib-text);
    padding: var(--lib-pt) 1rem var(--lib-pb);
  }
  .lib-reviews-summary__inner {
    max-width: 1200px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.75rem;
    text-align: left;
  }
  .lib-reviews-summary--full .lib-reviews-summary__inner { max-width: none; }
  .lib-reviews-summary__inner[data-align='center'] { align-items: center; text-align: center; }
  .lib-reviews-summary__inner[data-align='right'] { align-items: flex-end; text-align: right; }
  @media (max-width: 749px) {
    .lib-reviews-summary { padding-top: var(--lib-pt-m); padding-bottom: var(--lib-pb-m); }
    .lib-reviews-summary[data-show='desktop'] { display: none; }
    .lib-reviews-summary__inner[data-align-m='left'] { align-items: flex-start; text-align: left; }
    .lib-reviews-summary__inner[data-align-m='center'] { align-items: center; text-align: center; }
    .lib-reviews-summary__inner[data-align-m='right'] { align-items: flex-end; text-align: right; }
  }
  @media (min-width: 750px) {
    .lib-reviews-summary[data-show='mobile'] { display: none; }
  }
  .lib-reviews-summary__app { width: 100%; }

  .lib-reviews-summary__heading { margin: 0; font-size: clamp(1.5rem, 2.6vw, 2rem); line-height: 1.15; letter-spacing: -0.02em; }
  .lib-reviews-summary__heading--small { font-size: clamp(1.125rem, 1.8vw, 1.375rem); }
  .lib-reviews-summary__heading--large { font-size: clamp(1.9rem, 3.6vw, 2.75rem); }

  .lib-reviews-summary__summary { display: inline-flex; color: inherit; text-decoration: none; border-radius: 8px; }
  a.lib-reviews-summary__summary:hover .lib-reviews-summary__link { text-decoration-thickness: 2px; }
  a.lib-reviews-summary__summary:focus-visible { outline: 2px solid currentColor; outline-offset: 4px; }
  .lib-reviews-summary__visual {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 0.6rem;
    min-height: 24px;
    font-size: 0.9375rem;
  }
  .lib-reviews-summary[data-style='card'] .lib-reviews-summary__visual {
    flex-direction: column;
    gap: 0.35rem;
    padding: 1.5rem 2rem;
    border-radius: 16px;
    background: var(--lib-card);
  }
  .lib-reviews-summary[data-style='card'] .lib-reviews-summary__value { order: -1; font-size: 3rem; line-height: 1; letter-spacing: -0.03em; }
  .lib-reviews-summary[data-style='card'] .lib-reviews-summary__max { display: none; }

  .lib-reviews-summary__stars { position: relative; display: inline-block; line-height: 0; }
  .lib-reviews-summary__row { display: inline-flex; gap: 2px; }
  .lib-reviews-summary__row svg { width: var(--lib-star-size); height: var(--lib-star-size); fill: currentColor; }
  .lib-reviews-summary__row--empty { color: var(--lib-star-empty); }
  .lib-reviews-summary__row--full {
    position: absolute;
    inset: 0;
    color: var(--lib-star);
    clip-path: inset(0 calc(100% - var(--lib-fill)) 0 0);
  }
  .lib-reviews-summary__value { font-weight: 700; font-variant-numeric: tabular-nums; }
  .lib-reviews-summary__max { opacity: 0.7; margin-inline-start: -0.35rem; }
  .lib-reviews-summary__count { opacity: 0.85; }
  .lib-reviews-summary__link { font-weight: 600; text-decoration: underline; text-underline-offset: 0.25em; }
  .lib-reviews-summary__hint {
    margin: 0;
    max-width: 40rem;
    padding: 0.85rem 1rem;
    border: 1px dashed color-mix(in srgb, var(--lib-text) 40%, transparent);
    border-radius: 8px;
    font-size: 0.875rem;
    line-height: 1.5;
  }
  .lib-reviews-summary__sr {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  @media (prefers-reduced-motion: no-preference) {
    .lib-reviews-summary[data-animate] .lib-reviews-summary__row--full { animation: lib-reviews-summary-fill 1s 0.2s ease-out both; }
  }
  @keyframes lib-reviews-summary-fill { from { clip-path: inset(0 100% 0 0); } }
{% endstylesheet %}

{% schema %}
{
  "name": "Lib · Reviews summary",
  "tag": "section",
  "class": "lib-section",
  "settings": [
    {
      "type": "paragraph",
      "content": "Shows the star rating your review app saves in the reviews.rating and reviews.rating_count product metafields (most review apps do). Without a rating it shows nothing to shoppers, never sample ratings."
    },
    { "type": "header", "content": "Content" },
    { "type": "product", "id": "product", "label": "Product", "info": "Leave empty on product templates to use the page's product." },
    { "type": "inline_richtext", "id": "heading", "label": "Heading", "info": "Optional." },
    {
      "type": "select",
      "id": "heading_tag",
      "label": "Heading tag",
      "info": "Use H1 only when this is the page's main heading.",
      "options": [
        { "value": "h1", "label": "H1" },
        { "value": "h2", "label": "H2" },
        { "value": "h3", "label": "H3" },
        { "value": "p", "label": "Paragraph" }
      ],
      "default": "h2"
    },
    {
      "type": "select",
      "id": "heading_size",
      "label": "Heading size",
      "options": [
        { "value": "small", "label": "Small" },
        { "value": "medium", "label": "Medium" },
        { "value": "large", "label": "Large" }
      ],
      "default": "medium"
    },
    { "type": "checkbox", "id": "show_count", "label": "Show review count", "default": true },
    { "type": "checkbox", "id": "show_max", "label": "Show the scale (/ 5)", "default": false },
    { "type": "text", "id": "reviews_link", "label": "Link to the reviews", "info": "Optional. Usually the anchor of your review app's section, e.g. #reviews.", "default": "#reviews" },
    { "type": "text", "id": "link_label", "label": "Link text", "default": "Read reviews" },

    { "type": "header", "content": "Words" },
    { "type": "text", "id": "label_rated", "label": "Screen reader: before the rating", "default": "Rated" },
    { "type": "text", "id": "label_out_of", "label": "Screen reader: between rating and scale", "default": "out of" },
    { "type": "text", "id": "label_review", "label": "Review count word, one", "default": "review" },
    { "type": "text", "id": "label_reviews", "label": "Review count word, more than one", "default": "reviews" },

    { "type": "header", "content": "Layout" },
    {
      "type": "select",
      "id": "style",
      "label": "Style",
      "options": [
        { "value": "inline", "label": "One line" },
        { "value": "card", "label": "Card with large number" }
      ],
      "default": "inline"
    },
    { "type": "range", "id": "star_size", "label": "Star size", "min": 14, "max": 40, "step": 2, "unit": "px", "default": 20 },
    {
      "type": "select",
      "id": "section_width",
      "label": "Width",
      "options": [
        { "value": "page", "label": "Page width" },
        { "value": "full", "label": "Full width" }
      ],
      "default": "page"
    },
    { "type": "text_alignment", "id": "text_alignment", "label": "Alignment on desktop", "default": "left" },
    {
      "type": "select",
      "id": "text_alignment_mobile",
      "label": "Alignment on mobile",
      "options": [
        { "value": "left", "label": "Left" },
        { "value": "center", "label": "Center" },
        { "value": "right", "label": "Right" }
      ],
      "default": "left"
    },

    { "type": "header", "content": "Colors" },
    { "type": "color", "id": "color_bg", "label": "Background", "default": "#FFFDF9" },
    { "type": "color_background", "id": "color_bg_gradient", "label": "Background gradient", "info": "Optional. Covers the background color." },
    { "type": "color", "id": "color_text", "label": "Text", "default": "#1C1917" },
    { "type": "color", "id": "color_star", "label": "Stars", "default": "#B7791F" },
    { "type": "color", "id": "color_star_empty", "label": "Empty stars", "default": "#D9D2C5" },
    { "type": "color", "id": "color_card", "label": "Card background", "default": "#F5F2EC" },

    { "type": "header", "content": "Spacing and visibility" },
    { "type": "range", "id": "padding_top", "label": "Top padding on desktop", "min": 0, "max": 200, "step": 4, "unit": "px", "default": 24 },
    { "type": "range", "id": "padding_bottom", "label": "Bottom padding on desktop", "min": 0, "max": 200, "step": 4, "unit": "px", "default": 24 },
    { "type": "range", "id": "padding_top_mobile", "label": "Top padding on mobile", "min": 0, "max": 160, "step": 4, "unit": "px", "default": 16 },
    { "type": "range", "id": "padding_bottom_mobile", "label": "Bottom padding on mobile", "min": 0, "max": 160, "step": 4, "unit": "px", "default": 16 },
    {
      "type": "select",
      "id": "show_on",
      "label": "Show on",
      "options": [
        { "value": "all", "label": "All devices" },
        { "value": "desktop", "label": "Desktop only" },
        { "value": "mobile", "label": "Mobile only" }
      ],
      "default": "all"
    },
    { "type": "text", "id": "anchor_id", "label": "Anchor ID", "info": "Optional. Link to this section with #your-id." },

    { "type": "header", "content": "Motion" },
    { "type": "checkbox", "id": "animate", "label": "Fill the stars on load", "info": "Always off for visitors who prefer reduced motion.", "default": false }
  ],
  "blocks": [
    { "type": "@app" }
  ],
  "presets": [
    { "name": "Lib · Reviews summary" }
  ]
}
{% endschema %}
```

## License

MIT License, © 2026 Lintel. Free to use, change and ship in your own store or in client work; keep the license line at the top of each file.
