@okendo/shopify-hydrogen
v2.1.5
Published
Okendo React components for Shopify Hydrogen 2 (Remix)
Downloads
2,159
Maintainers
Keywords
Readme
Note: this package is to be used on stores built with Hydrogen v2, based on Remix. If your store is built with the now deprecated Hydrogen v1, please use version 1 of this package.
Okendo Hydrogen 2 (Remix) React Components
This package brings Okendo's review widgets to a Shopify Hydrogen store.
Requirements
- A Shopify store with the Okendo: Product Reviews & UCG app installed and configured.
- For existing merchants, your store must be upgraded to Okendo's Widget Plus widgets. It is free to upgrade. For more information please contact Okendo Support.
- A current Okendo subscription.
- A Storefront access token.
- A Shopify Hydrogen app.
Demo Store
Our demo store, which is based on the demo store provided by Shopify, can be found here.
Note: there have been multiple versions of Shopify's Hydrogen demo store. If your project is based on an old version of it, consult our version history to find out how to add Okendo to it.
How it works
This package provides:
- one function:
getOkendoProviderData
- three React components:
OkendoProvider
,OkendoStarRating
, andOkendoReviews
The function getOkendoProviderData
needs to be called in the loader
function of the root.tsx
file in the Hydrogen 2 store. The data is then retrieved in App
through useLoaderData
and provided to OkendoProvider
which is added in the body
of the HTML returned by App
.
Then, the components OkendoStarRating
and OkendoReviews
can be added on the store pages. There are a few more bits of configuration to do, please see below.
Expose Shopify Metafields
Okendo Reviews use Product and Shop metafields. You will need to expose these metafields so that they can be retrieved by your Hydrogen app.
At the moment, Shopify does not have a way of exposing Shop Metafields through their admin UI, so the preferred method is to contact Okendo Support.
Exposing Metafields via GraphQL
Using Curl
You can also expose the required Okendo Shopify metafields by using GraphQL with curl.
- Open a new terminal or PowerShell window.
- Run the following command to expose the
WidgetPreRenderStyleTags
shop metafield:
curl -X POST \
https://{shop}.myshopify.com/admin/api/2022-04/graphql.json \
-H 'Content-Type: application/graphql' \
-H 'X-Shopify-Access-Token: {access_token}' \
-d '
mutation {
metafieldStorefrontVisibilityCreate(
input: {
namespace: "okendo"
key: "WidgetPreRenderStyleTags"
ownerType: SHOP
}
) {
metafieldStorefrontVisibility {
id
}
userErrors {
field
message
}
}
}
'
- Run the following command to expose the
WidgetPreRenderBodyStyleTags
shop metafield:
curl -X POST \
https://{shop}.myshopify.com/admin/api/2022-04/graphql.json \
-H 'Content-Type: application/graphql' \
-H 'X-Shopify-Access-Token: {access_token}' \
-d '
mutation {
metafieldStorefrontVisibilityCreate(
input: {
namespace: "okendo"
key: "WidgetPreRenderBodyStyleTags"
ownerType: SHOP
}
) {
metafieldStorefrontVisibility {
id
}
userErrors {
field
message
}
}
}
'
- Run the following command to expose the
ReviewsWidgetSnippet
product metafield:
curl -X POST \
https://{shop}.myshopify.com/admin/api/2022-04/graphql.json \
-H 'Content-Type: application/graphql' \
-H 'X-Shopify-Access-Token: {access_token}' \
-d '
mutation {
metafieldStorefrontVisibilityCreate(
input: {
namespace: "okendo"
key: "ReviewsWidgetSnippet"
ownerType: PRODUCT
}
) {
metafieldStorefrontVisibility {
id
}
userErrors {
field
message
}
}
}
'
- Run the following command to expose the
StarRatingSnippet
the product metafield:
curl -X POST \
https://{shop}.myshopify.com/admin/api/2022-04/graphql.json \
-H 'Content-Type: application/graphql' \
-H 'X-Shopify-Access-Token: {access_token}' \
-d '
mutation {
metafieldStorefrontVisibilityCreate(
input: {
namespace: "okendo"
key: "StarRatingSnippet"
ownerType: PRODUCT
}
) {
metafieldStorefrontVisibility {
id
}
userErrors {
field
message
}
}
}
'
Using GraphQL IDE
- Open your GraphQL IDE (such as Postman) and make a
POST
request with the following details:
- URL: https://{shop}.myshopify.com/admin/api/2022-04/graphql.json
- Headers: - X-Shopify-Access-Token: {access_token} - Content-Type: application/json
- Execute the following request to expose the
WidgetPreRenderStyleTags
shop metafield:
mutation {
metafieldStorefrontVisibilityCreate(
input: {
namespace: "okendo"
key: "WidgetPreRenderStyleTags"
ownerType: SHOP
}
) {
metafieldStorefrontVisibility {
id
}
userErrors {
field
message
}
}
}
- Execute the following request to expose the
WidgetPreRenderBodyStyleTags
shop metafield:
mutation {
metafieldStorefrontVisibilityCreate(
input: {
namespace: "okendo"
key: "WidgetPreRenderBodyStyleTags"
ownerType: SHOP
}
) {
metafieldStorefrontVisibility {
id
}
userErrors {
field
message
}
}
}
- Execute the following request to expose the
ReviewsWidgetSnippet
product metafield:
mutation {
metafieldStorefrontVisibilityCreate(
input: {
namespace: "okendo"
key: "ReviewsWidgetSnippet"
ownerType: PRODUCT
}
) {
metafieldStorefrontVisibility {
id
}
userErrors {
field
message
}
}
}
- Execute the following request to expose the
StarRatingSnippet
the product metafield:
mutation {
metafieldStorefrontVisibilityCreate(
input: { namespace: "okendo", key: "StarRatingSnippet", ownerType: PRODUCT }
) {
metafieldStorefrontVisibility {
id
}
userErrors {
field
message
}
}
}
References
- https://shopify.dev/api/examples/metafields#step-1-expose-metafields
- https://shopify.dev/api/admin-graphql/2022-04/mutations/metafieldstorefrontvisibilitycreate
Installation
The code examples provided in this section are based on the Shopify template store created by running
npm create @shopify/hydrogen@latest
(see Shopify's documentation). You will find the following steps already done in our demo store.
Run:
npm i @okendo/shopify-hydrogen
app/root.tsx
Open app/root.tsx
and add the following import:
import {
OkendoProvider,
getOkendoProviderData,
} from "@okendo/shopify-hydrogen";
Locate the loader
function, append okendoProviderData
to the returned data as shown below, and set subscriberId
to your Okendo subscriber ID:
return defer(
{
...
okendoProviderData: await getOkendoProviderData({
context,
subscriberId: "<your-okendo-subscriber-id>",
}),
}
);
Locate the App
function, add the meta
tag oke:subscriber_id
to head
, and place your Okendo subscriber ID in its content:
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="oke:subscriber_id" content="<your-okendo-subscriber-id>" />
...
Append OkendoProvider
to body
, and pass it the data returned by getOkendoProviderData
. If Content Security Policy is active in your project, you also need to provide the nonce
(available with const nonce = useNonce()
in Shopify's Hydrogen demo store):
...
<body>
<OkendoProvider
nonce={nonce}
okendoProviderData={data.okendoProviderData}
/>
...
</body>
...
app/entry.server.tsx
This is only necessary if Content Security Policy is active in your project.
Locate the call to createContentSecurityPolicy
, and add:
https://d3hw6dc1ow8pp2.cloudfront.net
,https://d3g5hqndtiniji.cloudfront.net
, anddata:
todefaultSrc
https://d3hw6dc1ow8pp2.cloudfront.net
tostyleSrc
https://api.okendo.io
toconnectSrc
Note that it's necessary to to add the default values ('self'
, etc.) when extending the CSP. The call to createContentSecurityPolicy
should now look like the following:
const { nonce, header, NonceProvider } = createContentSecurityPolicy({
defaultSrc: [
"'self'",
"localhost:*",
"https://cdn.shopify.com",
"https://d3hw6dc1ow8pp2.cloudfront.net",
"https://d3g5hqndtiniji.cloudfront.net",
"https://cdn-static.okendo.io",
"https://surveys.okendo.io",
"data:",
],
imgSrc: [
"'self'",
"https://cdn.shopify.com",
"data:",
"https://d3hw6dc1ow8pp2.cloudfront.net",
"https://d3g5hqndtiniji.cloudfront.net",
"https://cdn-static.okendo.io",
"https://surveys.okendo.io",
],
mediaSrc: [
"'self'",
"https://d3hw6dc1ow8pp2.cloudfront.net",
"https://d3g5hqndtiniji.cloudfront.net",
"https://cdn-static.okendo.io"
],
styleSrcElem: [
"'self'",
"'unsafe-inline'",
"https://cdn.shopify.com",
"https://fonts.googleapis.com",
"https://fonts.gstatic.com",
"https://d3hw6dc1ow8pp2.cloudfront.net",
"https://cdn-static.okendo.io",
"https://surveys.okendo.io",
],
scriptSrc: [
"'self'",
"https://cdn.shopify.com",
"https://d3hw6dc1ow8pp2.cloudfront.net",
"https://cdn-static.okendo.io",
"https://surveys.okendo.io",
],
fontSrc: [
"'self'",
"https://fonts.gstatic.com",
"https://d3hw6dc1ow8pp2.cloudfront.net",
"https://cdn.shopify.com",
"https://cdn-static.okendo.io",
"https://surveys.okendo.io",
],
connectSrc: [
"'self'",
"https://monorail-edge.shopifysvc.com",
"localhost:*",
"ws://localhost:*",
"ws://127.0.0.1:*",
"https://api.okendo.io",
"https://cdn-static.okendo.io",
"https://surveys.okendo.io",
],
});
app/routes/_index.tsx
Add the following imports:
import {
OkendoStarRating,
type WithOkendoStarRatingSnippet,
} from "@okendo/shopify-hydrogen";
Add the following block just before the RECOMMENDED_PRODUCTS_QUERY
GraphQL query:
const OKENDO_PRODUCT_STAR_RATING_FRAGMENT = `#graphql
fragment OkendoStarRatingSnippet on Product {
okendoStarRatingSnippet: metafield(
namespace: "okendo"
key: "StarRatingSnippet"
) {
value
}
}
` as const;
Then append ${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
and ...OkendoStarRatingSnippet
to RECOMMENDED_PRODUCTS_QUERY
:
const RECOMMENDED_PRODUCTS_QUERY = `#graphql
${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
fragment RecommendedProduct on Product {
id
title
handle
priceRange {
minVariantPrice {
amount
currencyCode
}
}
images(first: 1) {
nodes {
id
url
altText
width
height
}
}
...OkendoStarRatingSnippet
}
query RecommendedProducts ($country: CountryCode, $language: LanguageCode)
@inContext(country: $country, language: $language) {
products(first: 4, sortKey: UPDATED_AT, reverse: true) {
nodes {
...RecommendedProduct
}
}
}
` as const;
Tweak the type of the products
prop of RecommendedProducts
:
products: Promise<{
products: {
nodes: (RecommendedProductsQuery["products"]["nodes"][0] &
WithOkendoStarRatingSnippet)[];
};
}>;
Add OkendoStarRating
to RecommendedProducts
:
<OkendoStarRating
productId={product.id}
okendoStarRatingSnippet={product.okendoStarRatingSnippet}
/>
For instance, we can add it below the product title, like this:
<Image
data={product.images.nodes[0]}
aspectRatio="1/1"
sizes="(min-width: 45em) 20vw, 50vw"
/>
<h4>{product.title}</h4>
<OkendoStarRating
productId={product.id}
okendoStarRatingSnippet={product.okendoStarRatingSnippet}
/>
<small>
<Money data={product.priceRange.minVariantPrice} />
</small>
We now have the Okendo Star Rating widget visible on our page:
app/routes/products.$handle.tsx
Add the following imports:
import {
OKENDO_PRODUCT_REVIEWS_FRAGMENT,
OKENDO_PRODUCT_STAR_RATING_FRAGMENT,
OkendoReviews,
OkendoStarRating,
type WithOkendoReviewsSnippet,
type WithOkendoStarRatingSnippet,
} from "@okendo/shopify-hydrogen";
Add the following block just before the RECOMMENDED_PRODUCTS_QUERY
GraphQL query:
const OKENDO_PRODUCT_STAR_RATING_FRAGMENT = `#graphql
fragment OkendoStarRatingSnippet on Product {
okendoStarRatingSnippet: metafield(
namespace: "okendo"
key: "StarRatingSnippet"
) {
value
}
}
` as const;
const OKENDO_PRODUCT_REVIEWS_FRAGMENT = `#graphql
fragment OkendoReviewsSnippet on Product {
okendoReviewsSnippet: metafield(
namespace: "okendo"
key: "ReviewsWidgetSnippet"
) {
value
}
}
` as const;
Then append ${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
, ${OKENDO_PRODUCT_REVIEWS_FRAGMENT}
, ...OkendoStarRatingSnippet
, and ...OkendoReviewsSnippet
to PRODUCT_FRAGMENT
:
const PRODUCT_FRAGMENT = `#graphql
${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
${OKENDO_PRODUCT_REVIEWS_FRAGMENT}
fragment Product on Product {
id
title
vendor
handle
descriptionHtml
description
options {
name
values
}
selectedVariant: variantBySelectedOptions(selectedOptions: $selectedOptions) {
...ProductVariant
}
variants(first: 1) {
nodes {
...ProductVariant
}
}
seo {
description
title
}
...OkendoStarRatingSnippet
...OkendoReviewsSnippet
}
${PRODUCT_VARIANT_FRAGMENT}
` as const;
Add OkendoReviews
to Product
:
<OkendoReviews
productId={product.id}
okendoReviewsSnippet={product.okendoReviewsSnippet}
/>
For instance, we can add it below the product section, like this:
<>
<div className="product">
<ProductImage image={selectedVariant?.image} />
<ProductMain
selectedVariant={selectedVariant}
product={product}
variants={variants}
/>
</div>
<OkendoReviews
productId={product.id}
okendoReviewsSnippet={product.okendoReviewsSnippet}
/>
</>
Tweak the type of the product
prop of ProductMain
:
product: ProductFragment &
WithOkendoStarRatingSnippet &
WithOkendoReviewsSnippet;
Add OkendoStarRating
to ProductMain
:
<OkendoStarRating
productId={product.id}
okendoStarRatingSnippet={product.okendoStarRatingSnippet}
/>
For instance, we can add it below the product title, like this:
<div className="product-main">
<h1>{title}</h1>
<OkendoStarRating
productId={product.id}
okendoStarRatingSnippet={product.okendoStarRatingSnippet}
/>
<ProductPrice selectedVariant={selectedVariant} />
We now have the Okendo Star Rating and Reviews widgets visible on our product page: