npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@okendo/shopify-hydrogen

v2.1.5

Published

Okendo React components for Shopify Hydrogen 2 (Remix)

Downloads

2,159

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

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, and OkendoReviews

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.

  1. Open a new terminal or PowerShell window.
  2. 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
		}
	}
}
'
  1. 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
		}
	}
}
'
  1. 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
		}
	}
}
'
  1. 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

  1. 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
  1. Execute the following request to expose the WidgetPreRenderStyleTags shop metafield:
mutation {
	metafieldStorefrontVisibilityCreate(
		input: {
			namespace: "okendo"
			key: "WidgetPreRenderStyleTags"
			ownerType: SHOP
		}
	) {
		metafieldStorefrontVisibility {
			id
		}
		userErrors {
			field
			message
		}
	}
}
  1. Execute the following request to expose the WidgetPreRenderBodyStyleTags shop metafield:
mutation {
	metafieldStorefrontVisibilityCreate(
		input: {
			namespace: "okendo"
			key: "WidgetPreRenderBodyStyleTags"
			ownerType: SHOP
		}
	) {
		metafieldStorefrontVisibility {
			id
		}
		userErrors {
			field
			message
		}
	}
}
  1. Execute the following request to expose the ReviewsWidgetSnippet product metafield:
mutation {
	metafieldStorefrontVisibilityCreate(
		input: {
			namespace: "okendo"
			key: "ReviewsWidgetSnippet"
			ownerType: PRODUCT
		}
	) {
		metafieldStorefrontVisibility {
			id
		}
		userErrors {
			field
			message
		}
	}
}
  1. 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

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, and data: to defaultSrc
  • https://d3hw6dc1ow8pp2.cloudfront.net to styleSrc
  • https://api.okendo.io to connectSrc

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:

Okendo's Star Rating widget

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:

Okendo's Star Rating and Reviews widgets