Developers
FiveOh’s data is plain Shopify data, so you are not limited to our blocks. Build review components into your own sections and snippets, show ratings in places no app block can reach, or read everything through the Storefront API in a headless store.
This page explains how to read the data. Copy-paste starting points, each with a preview, are on the page of the matching block:
- Ratings: Product Rating, Product Card Ratings, Rating Bar, Floating Badge.
- Reviews: Review List with sorting, filters and load more, AI summary and rating summary; Reviews Slider; featured reviews, replies, the verified badge.
- Forms: Review Form and a “Write a review” button.
- Signals: units sold, bestseller rank and 5-star share, the fit slider.
The examples write their texts in English; Languages shows how to translate them. Every metafield and field is listed in the reference.
Reading the data in Liquid
FiveOh’s metafields are in the namespace app--324569563137. Assign it once and read from it:
{%- assign fiveoh = product.metafields['app--324569563137'] -%}
{{ fiveoh.rating.value.rating }} ({{ fiveoh.rating_count.value }} reviews)The same works on variant, shop, customer and order, each with the values that make sense there. Inside our own blocks the namespace is written $app instead; both point to the same data.
A few things to know:
.valueturns a metafield into data. A rating becomes an object with.rating, a JSON metafield an object or list you walk with dots, and a list of reviews a list of metaobjects.- Reviews come in pairs. A review list like
newest_reviewsholds theproduct_reviewmetaobjects, andnewest_reviews_metathe matchingreview_metawith the translated text. Entryiof one belongs to entryiof the other. - Liquid shows up to 50 entries of a list. The lists hold up to 250 reviews and
.sizecounts them all; to show more than 50, load them with<fo-reviews>, see below. - Liquid output is cached. Shopify caches rendered pages, so anything relative to the current time, like “2 days ago”, must be computed in the browser with your own JavaScript. Render the date in Liquid, for example in a
datetimeattribute, and turn it into a relative time there.
A list of reviews
This shows the five newest reviews of a product, each with its stars, translated text and author:
{%- assign fiveoh = product.metafields['app--324569563137'] -%}
{%- assign metas = fiveoh.newest_reviews_meta.value | compact -%}
{%- for review in fiveoh.newest_reviews.value limit: 5 -%}
{%- assign meta = metas[forloop.index0] -%}
<article>
<span class="fo-stars" style="--fo-rating: {{ review.rating.value.rating }}"></span>
<h3>{{ meta.title.value }}</h3>
<p>{{ meta.body.value }}</p>
<p>{{ review.author_display_name.value | default: 'Anonymous' }}</p>
</article>
{%- endfor -%}The | compact turns the meta list into an array, which Liquid needs to look up an entry by its position. The fo-stars span draws five stars filled to the rating; its styles come with our blocks and the Product Card Ratings embed.
Making it interactive
Rendered this way, the reviews are complete without JavaScript. To add load more, sorting, filters, search or a review form, wrap your markup in FiveOh’s custom elements and mark the controls with data-fo-* attributes:
<fo-reviews data-fo-product-id="{{ product.id }}" data-fo-page-size="5">
<div data-fo-items>
{%- comment -%} the reviews from above {%- endcomment -%}
</div>
<template data-fo-template="review">
<article>
<h3 data-fo-field="title"></h3>
<p data-fo-field="body"></p>
</article>
</template>
<button data-fo-action="load-more" data-fo-if="has_next_page">Load more</button>
</fo-reviews>The script loads further reviews into data-fo-items, filling the <template> for each one. It reacts only to the elements and attributes, never to your classes, so the markup and styling stay entirely yours. This needs the FiveOh Core app embed. Every element, action and attribute is in HTML & JS.
Headless storefronts
The metafields and metaobjects are readable through the Storefront API. Use the full namespace:
query ProductReviews($handle: String!) {
product(handle: $handle) {
rating: metafield(namespace: "app--324569563137", key: "rating") { value }
count: metafield(namespace: "app--324569563137", key: "rating_count") { value }
reviews: metafield(namespace: "app--324569563137", key: "newest_reviews") {
references(first: 10) {
nodes {
... on Metaobject {
handle
rating: field(key: "rating") { value }
author: field(key: "author_display_name") { value }
}
}
}
}
}
}For the translated text, query the matching newest_reviews_meta list with the @inContext(language: …) directive. JSON values arrive as strings: parse them, then use the structures in the reference.
Customer accounts
Customer account UI extensions can read the metafields on the customer, the order and the shop:
review_handleson the customer maps every product and variant the customer reviewed to the review’s handle, so you can show “You reviewed this” or link to the review.review_request_urlon the order links to the post-purchase form for that order, for a “Write a review” button in the order details.rating,newest_reviewsand the other rating values on the customer and the order show the reviews they wrote.
Declare the metafields your extension reads in its shopify.extension.toml.
What you can rely on
Everything listed on Metaobjects & metafields and HTML & JS only grows: nothing there is ever renamed, removed or given a new meaning. Code you write against it keeps working through every update. These are the rules behind that promise:
- Add only. Nothing listed here is ever renamed, removed, or given a new meaning or scale. New metafields, fields, elements and attributes may be added at any time.
- Everything else is internal and may change without notice: CSS classes (except
fo-stars), the endpoint the script loads reviews from, the app’s settings, and the data in FiveOh’s own database. - Server-rendered first. Reviews and ratings are rendered with Liquid on the server. JavaScript only adds interaction, so without it everything stays readable.
- Works with content loaded later. Everything keeps working when the theme inserts it after the page loaded: predictive search, quick view, infinite scroll, the cart drawer and the Theme Editor.
- Namespace. In theme code, FiveOh’s metafields are in the namespace
app--324569563137. Inside FiveOh’s own blocks the same namespace is written$app. - The FiveOh Core app embed. The interactive parts need the FiveOh Core app embed. Parts that are only Liquid work without it.
- Translations. Review text comes translated: read it from the
review_meta. Attribute labels come in every shop language, inreview_attribute_definitions. Texts of your own theme code belong in the theme’s locale files, read with| t. - Texts in FiveOh’s blocks. The blocks have no locale files. Every text they show, including labels for screen readers and messages of the script, is in the block’s translatable Texts setting, one
key: textper line. The structure prints a text with{% render 'fo-text', key: 'review_count', a: count, plural_by: count %}:{a}to{j}in the text are replaced by the values passed, and a text with two forms,one|other, picks its form byplural_by. To use a text inside an HTML attribute, capture it and clean it with| strip_html | strip | escape, which also removes the comments Shopify wraps around snippets. The Structure setting itself is not translatable.
Reserved names
window.FiveOh, the fiveoh: event prefix, and every metafield and field marked Reserved in the tables above. Reserved names are not written yet, but will keep the meaning given here.