@revova/hydrogen
v1.1.0
Published
Official Revova review widgets for Shopify Hydrogen storefronts
Maintainers
Readme
@revova/hydrogen
Official Revova review widgets for Shopify Hydrogen storefronts.
Installation
npm install @revova/hydrogenRequirements
- React 18+
- Shopify Hydrogen storefront with the Revova app installed on the store
How it works
Components and hooks call your Revova backend directly over HTTPS with a shop query parameter and a Bearer token:
GET https://yourapp.trycloudflare.com/api/reviews?shop=yourstore.myshopify.com&...
Authorization: Bearer <apiToken>No Shopify App Proxy. No CORS issues. No API keys exposed beyond what you pass in.
Setup (Hydrogen)
1 — Add your credentials to the environment
# .env
PUBLIC_REVOVA_API_URL=https://yourapp.trycloudflare.com
PUBLIC_STORE_DOMAIN=yourstore.myshopify.com
PUBLIC_REVOVA_API_TOKEN=your_token_hereExpose them through your Hydrogen loader:
// app/root.tsx or any route loader
export async function loader({ context }: LoaderArgs) {
return {
apiUrl: context.env.PUBLIC_REVOVA_API_URL,
shop: context.env.PUBLIC_STORE_DOMAIN,
apiToken: context.env.PUBLIC_REVOVA_API_TOKEN,
};
}2 — Spread credentials into any component
Every component accepts apiUrl, shop, and apiToken. The easiest pattern is to spread a single creds object:
const creds = {
apiUrl: env.PUBLIC_REVOVA_API_URL,
shop: env.PUBLIC_STORE_DOMAIN,
apiToken: env.PUBLIC_REVOVA_API_TOKEN,
};
<ReviewWidget {...creds} productId={product.id} />Quick Start
// app/routes/products.$handle.tsx
import '@revova/hydrogen/styles.css'; // import once in app/root.tsx instead
import { ReviewWidget, FloatingReviewButton } from '@revova/hydrogen';
export async function loader({ context, params }: LoaderArgs) {
const product = await getProduct(params.handle);
return {
product,
apiUrl: context.env.PUBLIC_REVOVA_API_URL,
shop: context.env.PUBLIC_STORE_DOMAIN,
apiToken: context.env.PUBLIC_REVOVA_API_TOKEN,
};
}
export default function ProductPage() {
const { product, apiUrl, shop, apiToken } = useLoaderData<typeof loader>();
const creds = { apiUrl, shop, apiToken };
return (
<>
<h1>{product.title}</h1>
<ReviewWidget {...creds} productId={product.id} />
<FloatingReviewButton {...creds} productId={product.id} />
</>
);
}API Credentials (ApiProps)
All components and hooks require these three props. Spread them as a group for convenience.
| Prop | Type | Description |
|---|---|---|
| apiUrl | string | Revova backend base URL (e.g. https://yourapp.trycloudflare.com) |
| shop | string | Store myshopify.com domain (e.g. yourstore.myshopify.com) |
| apiToken | string | Bearer token for authorization |
Components
<ReviewWidget>
Full review list with pagination, sorting, and an inline submission form.
import { ReviewWidget } from '@revova/hydrogen';
<ReviewWidget
{...creds}
productId={product.id}
pageSize={10}
showForm
locale="fr"
/>| Prop | Type | Default | Description |
|---|---|---|---|
| ...creds | ApiProps | — | apiUrl, shop, apiToken |
| productId | string | — | Shopify product GID |
| locale | string | — | Locale code for translated reviews (e.g. fr) |
| pageSize | number | 10 | Reviews per page |
| showForm | boolean | true | Show the write-review button and form |
| starColor | string | #f59e0b | Star fill colour |
| className | string | — | CSS class on the wrapper |
<ReviewForm>
Standalone submission form — use when you want the form separate from the list.
import { ReviewForm } from '@revova/hydrogen';
// `form` comes from the ReviewsResponse returned by useReviews
<ReviewForm
{...creds}
productId={product.id}
form={data.form}
onSuccess={() => console.log('submitted')}
/>Props: ...creds, productId, form (required — ResolvedForm), onSuccess?, className?
<StarRating>
Renders star icons for any numeric rating. Supports half-stars via linear gradient fill.
import { StarRating } from '@revova/hydrogen';
<StarRating rating={4.5} size={20} color="#f59e0b" />Props: rating (0–5), max? (default 5), size? (px, default 16), color?, className?
<ReviewCount>
Aggregate rating + review count badge. Great for product cards and PDP headers.
import { ReviewCount } from '@revova/hydrogen';
<ReviewCount {...creds} starSize={14} />Props: ...creds, starColor?, starSize?, className?
<ReviewCarousel>
Auto-advancing carousel of recent reviews. Includes previous/next arrows and dot indicators.
import { ReviewCarousel } from '@revova/hydrogen';
<ReviewCarousel {...creds} limit={10} autoPlay intervalMs={4000} />Props: ...creds, limit? (default 10), autoPlay? (default true), intervalMs? (default 4000), starColor?, className?
<ReviewGallery>
Masonry photo grid of reviews that have images. Clicking any photo opens a lightbox with the full review.
import { ReviewGallery } from '@revova/hydrogen';
<ReviewGallery {...creds} columns={3} limit={20} />Props: ...creds, limit? (default 20), columns? (default 3), starColor?, className?
<QnAWidget>
Product Q&A — lists questions with answers and includes an ask-a-question form.
import { QnAWidget } from '@revova/hydrogen';
<QnAWidget {...creds} productId={product.id} />Props: ...creds, productId, className?
<TrustBadge>
Aggregate rating badge in three styles: pill (default), inline, or card.
import { TrustBadge } from '@revova/hydrogen';
<TrustBadge {...creds} style="pill" />
<TrustBadge {...creds} style="card" />
<TrustBadge {...creds} style="inline" />Props: ...creds, style? ('pill' | 'inline' | 'card', default 'pill'), starColor?, className?
<ReviewTicker>
A continuously scrolling marquee of recent review snippets — ideal for homepages and hero sections.
import { ReviewTicker } from '@revova/hydrogen';
<ReviewTicker {...creds} limit={20} speedSeconds={30} />Props: ...creds, limit? (default 20), speedSeconds? (default 30), starColor?, className?
<SocialProofPopup>
A timed floating popup showing recent reviews one at a time. Add once to your root layout.
import { SocialProofPopup } from '@revova/hydrogen';
<SocialProofPopup
{...creds}
position="bottom-left"
intervalMs={8000}
displayMs={5000}
/>Props: ...creds, position? ('bottom-left' | 'bottom-right', default 'bottom-left'), intervalMs? (default 8000), displayMs? (default 5000), starColor?, className?
<FloatingReviewsTab>
A sticky tab fixed to the left or right edge of the viewport. Clicking it slides out a panel of recent reviews.
import { FloatingReviewsTab } from '@revova/hydrogen';
<FloatingReviewsTab {...creds} label="Reviews" position="right" />Props: ...creds, label? (default 'Reviews'), position? ('left' | 'right', default 'right'), color?, limit? (default 5), starColor?, className?
<FloatingReviewButton>
A fixed "Write a Review" button that opens a modal with the submission form.
import { FloatingReviewButton } from '@revova/hydrogen';
<FloatingReviewButton
{...creds}
productId={product.id}
text="Write a Review"
position="bottom-right"
/>Props: ...creds, productId, text? (default 'Write a Review'), color?, position? ('bottom-left' | 'bottom-right', default 'bottom-right'), className?
Hooks
Use hooks to build fully custom UIs while Revova handles data fetching.
useReviews(options)
Paginated reviews for a product with sorting controls.
import { useReviews } from '@revova/hydrogen';
const { data, loading, error, setPage, setSort, currentPage, currentSort, refetch } = useReviews({
...creds,
productId: product.id,
limit: 5,
sort: 'helpful', // 'recent' | 'helpful' | 'rating_high' | 'rating_low'
locale: 'fr', // optional — returns translated content
});useWidgetGlobals(options)
Shop-level stats and recent reviews. Used internally by most global widgets.
import { useWidgetGlobals } from '@revova/hydrogen';
const { data, loading, error } = useWidgetGlobals({ ...creds, limit: 20 });
// data.stats.averageRating — "4.8"
// data.stats.totalReviews — 142
// data.reviews — recent GlobalReview[]
// data.config — merchant widget settingsuseSubmitReview(creds)
Submit a new review programmatically.
import { useSubmitReview } from '@revova/hydrogen';
const { submit, submitting, success, error, result, reset } = useSubmitReview(creds);
await submit({
productId: 'gid://shopify/Product/123',
email: '[email protected]',
formId: 'form-id-from-loader',
fieldAnswers: [
{ fieldId: 'star-field-id', value: 5 },
{ fieldId: 'title-field-id', value: 'Great product!' },
{ fieldId: 'body-field-id', value: 'Really happy with this purchase.' },
],
});useQnA(options)
Fetch and submit Q&A for a product.
import { useQnA } from '@revova/hydrogen';
const { data, loading, setPage, submitQuestion, submitAnswer, submitState, resetSubmit } = useQnA({
...creds,
productId: product.id,
});
await submitQuestion({
intent: 'question',
productId: product.id,
email: '[email protected]',
body: 'Does this come in blue?',
});useHelpfulVote(creds)
Cast a helpful / not-helpful vote on a review.
import { useHelpfulVote } from '@revova/hydrogen';
const { vote, loading, voted } = useHelpfulVote(creds);
<button onClick={() => vote({ reviewId: review.id, vote: 'helpful' })} disabled={voted}>
Helpful ({review.helpfulCount})
</button>Styling
The package ships a bundled CSS file. Import it once in your app root (e.g. app/root.tsx):
import '@revova/hydrogen/styles.css';This applies the full rv-* design system — cards, stars, modal, form, ticker animation, popup, gallery lightbox, and all widget layouts. All class names are prefixed rv- so they won't conflict with your theme styles.
To customise, pass a className prop on any component and override the relevant rv-* classes in your own CSS. The starColor prop (default #f59e0b) is available on every component that renders stars.
Placement Guide
| Widget | Where to use |
|---|---|
| <ReviewWidget> | Product detail page |
| <ReviewForm> | Custom PDP, post-purchase page |
| <StarRating> | Anywhere — product cards, PDPs, search results |
| <ReviewCount> | Product cards, PDP header |
| <ReviewCarousel> | Homepage, collection pages |
| <ReviewGallery> | Homepage, dedicated reviews page |
| <QnAWidget> | Product detail page (below reviews) |
| <TrustBadge> | Homepage hero, footer, landing pages |
| <ReviewTicker> | Homepage hero, announcement bar |
| <SocialProofPopup> | Root layout (renders once site-wide) |
| <FloatingReviewsTab> | Root layout (renders once site-wide) |
| <FloatingReviewButton> | Product detail page |
