Skip to main content

Growave React SDK Integration with Hydrogen Storefront

Introduction​

For easier integration of Growave into your headless or Hydrogen storefront, you can use our JavaScript library, available via npm:
@growave-io/react-sdk.

This document will guide you through the setup and integration process, using the SDK in a Hydrogen storefront with examples for initializing the SDK, handling customer authentication, and setting up custom cart and discount functionalities.

Unsupported environments

The Growave React SDK is designed to work only in web environments and requires access to the window object. This SDK must be used within a WebView when integrated into native mobile apps. Using the React SDK in environments like React Native without a WebView is not supported and will cause runtime errors.

Script Injection Requirement​

Before using the Growave React SDK, you must include the Growave bootstrap script in your HTML. This script loads the core application logic required for the SDK to function correctly.

Add the following <script> tag to your page before using the SDK:

<script src="https://static.growave.io/bootstrap_growave.js"></script>

Installation and Setup​

To install the Growave React SDK, run:

npm install @growave-io/react-sdk

Then, wrap your application in the <GrowaveProvider /> component to provide access to the SDK throughout your app. For example, in the App component:

import {
GrowaveApp,
Role,
GrowaveProvider,
OverlayPlaceholders,
generateSignature,
useGrowave,
} from "@growave-io/react-sdk";

export default function App() {
return (
<GrowaveProvider>
<Layout>
<Outlet />
</Layout>
</GrowaveProvider>
);
}

<GrowaveProvider /> stores the current state of the SDK integration, such as whether the SDK is ready to be used, and passes this state through context. You can access this state via the useGrowave hook.

Initializing the SDK​

Once the provider is in place, you can use the useGrowave hook to initialize and interact with the SDK. Below is an example of how you can initialize the SDK when certain data becomes available.

import { useEffect } from "react";
import { useRouteLoaderData } from "react-router";
import { useGrowave, GrowaveApp, Role } from "@growave-io/react-sdk";
import type { loader } from "~/root";

export default function YourComponent() {
const {
initGrowave,
isReadyForInit,
isInitialized,
isAuthenticationSDKReady,
authenticateCustomer,
logoutCustomer,
getCurrentRole,
} = useGrowave();
const data = useRouteLoaderData<typeof loader>("root");

// Initialize the Growave SDK once the data is available and the SDK is ready.
useEffect(() => {
if (data?.myshopifyDomain && isReadyForInit && !isInitialized) {
initGrowave({
myshopifyDomain: data.myshopifyDomain,
storefrontApiAccessToken: data.storefrontApiAccessToken,
enabledApps: [
GrowaveApp.Reviews,
GrowaveApp.Wishlist,
GrowaveApp.Rewards,
],
localization: "nl",
});
}
}, [
data?.myshopifyDomain,
data?.storefrontApiAccessToken,
initGrowave,
isReadyForInit,
isInitialized,
]);

// Authenticate the customer if they are logged in and the SDK is ready.
// The signature must be generated on your server using HMAC-SHA1:
// signature = sha1("{shopifyCustomerId}{signatureTimestamp}{secretKey}")
// Never expose your secret key in client-side code.
useEffect(() => {
if (
isAuthenticationSDKReady &&
data?.customerAuthentication &&
[Role.Guest, null].includes(getCurrentRole())
) {
authenticateCustomer({
shopifyCustomerId:
data.customerAuthentication.shopifyCustomerId,
signatureTimestamp: data.customerAuthentication.timestamp,
signature: data.customerAuthentication.signature,
});
}
}, [
isAuthenticationSDKReady,
data?.customerAuthentication,
authenticateCustomer,
getCurrentRole,
]);

// Log out the customer once the loader data says they are signed out.
// Wait for the data first: before it loads, `customerAuthentication` is
// missing for signed-in customers too.
useEffect(() => {
if (
isAuthenticationSDKReady &&
data &&
!data.customerAuthentication &&
[Role.Customer].includes(getCurrentRole())
) {
logoutCustomer();
}
}, [
isAuthenticationSDKReady,
data,
getCurrentRole,
logoutCustomer,
]);

return <>...</>;
}

You don't need to reload the page after logoutCustomer(). The next Growave request gets a guest token automatically.

If logoutCustomer() and authenticateCustomer() overlap, the call made last wins. A resolved authenticateCustomer() only tells you the request was processed; a later call may already have replaced its token. Check the current state with getCurrentRole().

Custom Cart Integration for Hydrogen​

When using Growave with a Hydrogen-based Custom Storefront, the standard Shopify endpoints /cart.js and /cart/add.js are not available. Therefore, custom API routes are needed to handle cart-related operations, such as retrieving and adding items to the cart and applying discount codes.

Using Custom API Routes​

To support cart operations in your Hydrogen storefront, create the following API routes:

  • api.addLines.tsx: Adds items to the cart.
  • api.getCart.tsx: Retrieves the current cart.
  • api.applyDiscount.tsx: Applies a discount code to the cart.

These routes use HydrogenUtils from @growave-io/react-sdk to convert the Shopify GraphQL cart data into a format compatible with Growave.

1. Add Lines to Cart (api.addLines.tsx)​

This route adds items to the cart. The HydrogenUtils.convertCart() method is used to transform the cart data.

import { HydrogenUtils } from "@growave-io/react-sdk";
import { cartLinesAddDefault } from "@shopify/hydrogen";
import { json, type ActionFunctionArgs } from "@shopify/remix-oxygen";

export async function action({ request, context }: ActionFunctionArgs) {
let cart = await context.cart.get();
if (!cart) {
await context.cart.create({ lines: [], discountCodes: [] });
cart = await context.cart.get();
}
if (!cart) throw new Error("Failed to create cart");

const setCartHeaders = context.cart.setCartId(cart.id);
const { items } = (await request.json()) as {
items: { variantID: string; quantity: number }[];
};

const cartAdd = cartLinesAddDefault({
storefront: context.storefront,
getCartId: () => cart.id,
});
const result = await cartAdd(
items.map((item) => ({
merchandiseId: `gid://shopify/ProductVariant/${item.variantID}`,
quantity: item.quantity,
})),
);

return json(
{ cart: HydrogenUtils.convertCart(await context.cart.get()) },
{
headers: setCartHeaders,
},
);
}

2. Get Cart (api.getCart.tsx)​

This route retrieves the current cart and transforms it using HydrogenUtils.convertCart().

import { type LoaderFunctionArgs } from "@shopify/remix-oxygen";
import { json } from "@shopify/remix-oxygen";
import { HydrogenUtils } from "@growave-io/react-sdk";

export async function loader({ request, context, params }: LoaderFunctionArgs) {
let cart = await context.cart.get();
if (!cart) {
await context.cart.create({ lines: [], discountCodes: [] });
cart = await context.cart.get();
}
if (!cart) throw new Error("Failed to create cart");

const setCartHeaders = context.cart.setCartId(cart.id);
return json(
{ cart: HydrogenUtils.convertCart(await context.cart.get()) },
{
headers: setCartHeaders,
},
);
}

3. Apply Discount (api.applyDiscount.tsx)​

This route applies a discount code to the cart.

import { cartDiscountCodesUpdateDefault } from "@shopify/hydrogen";
import { json, type ActionFunctionArgs } from "@shopify/remix-oxygen";
import { HydrogenUtils } from "@growave-io/react-sdk";

export async function action({ request, context }: ActionFunctionArgs) {
let cart = await context.cart.get();
if (!cart) {
await context.cart.create({ lines: [], discountCodes: [] });
cart = await context.cart.get();
}
if (!cart) throw new Error("Failed to create cart");

const setCartHeaders = context.cart.setCartId(cart.id);
const { discountCode } = (await request.json()) as { discountCode: string };

const cartDiscount = cartDiscountCodesUpdateDefault({
storefront: context.storefront,
getCartId: () => cart.id,
});

await cartDiscount([discountCode]);

return json(
{ cart: HydrogenUtils.convertCart(await context.cart.get()) },
{
headers: setCartHeaders,
},
);
}

Providing Custom Cart and Discount Methods to Growave​

To use these custom routes with Growave, set up the cart and discount methods via setCustomCartRepository and setCustomDiscountStrategy in Growave’s SDK.

Example​

import { useGrowave } from "@growave-io/react-sdk";

function RootComponent() {
const { sdk, isCartSDKReady, isDiscountsSDKReady } = useGrowave();

useEffect(() => {
if (isCartSDKReady) {
sdk.cart.methods.setCustomCartRepository({
async getCart() {
const { cart } = (await (
await fetch("/api/getCart")
).json()) as any;
return cart;
},
async addToCart(params: {
items: { variantID: number; quantity: number }[];
}) {
const { cart } = (await (
await fetch("/api/addLines", {
method: "POST",
body: JSON.stringify({ items: params.items }),
})
).json()) as any;
return { cart };
},
});
}
}, [isCartSDKReady, sdk]);

useEffect(() => {
if (isDiscountsSDKReady) {
sdk.discounts.methods.setCustomDiscountStrategy({
async applyDiscount(params: { discountCode: string }) {
await (
await fetch(`/api/applyDiscount`, {
method: "POST",
body: JSON.stringify({
discountCode: params.discountCode,
}),
})
).json();
},
});
}
}, [isDiscountsSDKReady, sdk]);

return <div>...</div>;
}

Summary​

  • Use setCustomCartRepository to specify custom methods for retrieving and adding items to the cart.
  • Use setCustomDiscountStrategy to define a custom method for applying discount codes.
  • HydrogenUtils.convertCart() simplifies converting the Shopify GraphQL cart to a Growave-compatible format.

Widget Placeholders​

The @growave-io/react-sdk provides several placeholder components for embedding widgets, such as reviews, wishlist, and rewards. To integrate widgets into your Hydrogen storefront, use the following components.

Make sure to add the <OverlayPlaceholders /> component in the <body> of your application. This ensures that the placeholders for overlay widgets (e.g., <RewardsPopUpWidget />) are rendered correctly:

<OverlayPlaceholders />

Here are some of the key widget components provided by the SDK:

<OverlayPlaceholders />​

  • Description: Adds placeholders for various overlay widget components, such as <RewardsPopUpWidget />.
  • Props: No props required.

<ReviewsAverageRatingWidget />​

  • Description: Displays the average product rating on the product page.
  • Props:
    • productId (number | string): The ID of the product for which the average rating is displayed. Can accept GID in the format gid://Shopify/Product/123.

<ReviewsMiniSliderWidget />​

  • Description: Displays a mini slider with customer reviews on the product page.
  • Props:
    • productId (number | string): The ID of the product for which the mini slider is shown. Can accept GID in the format gid://Shopify/Product/123.

<WishlistsAddToWishlistProductPageWidget />​

  • Description: Adds an "Add to Wishlist" button on the product page.
  • Props:
    • productId (number | string): The ID of the product. Can accept GID in the format gid://Shopify/Product/123.
    • variantId (number | string): The ID of the product variant. Can accept GID in the format gid://Shopify/ProductVariant/123.

<RewardsEarnPointsProductPageWidget />​

  • Description: Displays rewards points information on the product page.
  • Props:
    • productId (number | string): The ID of the product. Can accept GID in the format gid://Shopify/Product/123.
    • variantId (number | string): The ID of the product variant. Can accept GID in the format gid://Shopify/ProductVariant/123.

<RewardsReferralProgramPageWidget />​

  • Description: Shows the referral program widget on a dedicated page.
  • Props: No props required.

<RewardsVipTiersPageWidget />​

  • Description: Displays the VIP tiers widget on a dedicated rewards page.
  • Props: No props required.

This completes the documentation for integrating Growave with a custom Hydrogen storefront, covering SDK setup, custom cart management, and widget placeholders.