Integrate Joy Loyalty widget to Hydrogen

Hand-on guide on how to integrate Joy Loyalty seamlessly with Shopify Headless Commerce or Custom Storefront.

If you're building with Shopify's Hydrogen and want to level up your store with a loyalty program, you've come to the right place. Joy Loyalty is a great way to keep your customers coming back, and integrating it into your Hydrogen store is easier than you might think. In this guide, I’ll walk you through the steps to get Joy Loyalty up and running in your custom storefront, so you can start rewarding your customers in no time.

Enable Custom Storefront Development

Before diving in further, you must first enable our Custom Storefront Development in Settings > General. With this enabled, Shopify Storefront API can now access the Joy Metafield data.

Setting up your Hydrogen store

If you've already developed your Hydrogen store, you're all set! However, if you haven't started yet or are looking to test a Hydrogen store with Joy Loyalty, don't worry. You can easily set up a quick-start Hydrogen store by following this guide: Getting Started with Hydrogen.

If you are not on Shopify Plus plan but still want to test this out with development store first. You can update your .env file with Storefront API key from your Custom App

SESSION_SECRET="foobar"
PUBLIC_STOREFRONT_API_TOKEN="YOUR_KEY"
PUBLIC_STOREFRONT_API_VERSION="2023-04"
PUBLIC_STORE_DOMAIN="YOUR_STORE.myshopify.com"

Integrating Joy Widget

In this guide, we will use the latest Hydrogen default template, if you are using an older version of Hydrogen, we hope that you can understand the idea of the code implementing and do it accordingly. If you still have a problem, feel free to reach out to our support team, our support team will get our developer team involved to assist.

You can find all the codes related to this guide on this GitHub repository.

To display our Joy Loyalty program widget on the store, we will need a global variable AVADA_JOY and the avada-joy.min.js file to run on your storefront. The AVADA_JOY variable will use the value of the Joy loyalty metafield.

In the root.jsx file, you need to update the root loader, which is the loader function that runs on every page. This loader runs to request global data. Since our Joy loyalty SDK run on every pages, so we will need to use the root loader.

Here is the Graphql query for all the Joy loyalty program data at app/graphql/joy-loyalty/JoyShopDataQuery.js

export const JOY_SHOP_DATA_QUERY = `#graphql
query {
    localization {
        language {
            name
            isoCode
        }
        country {
            currency {
                isoCode
            }
            isoCode
        }
    }
    shop {
        metafield (namespace: "joy_loyalty_avada", key: "data"){
            value
        }
    }
}
`;

Then, you can update the loadCriticalData function to the below code snippet. This function will look different between different versions of Hydrogen, but the idea remains the same. Find the root loader functions, and call the storefront API to retrieve the metafield value of Joy Loyalty there along with Shop information about currency, locale, and country.

/**
 * Load data necessary for rendering content above the fold. This is the critical data
 * needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
 * @param {LoaderFunctionArgs}
 */
async function loadCriticalData({context}) {
  const {storefront} = context;

  const [header, joyApiData] = await Promise.all([
    storefront.query(HEADER_QUERY, {
      cache: storefront.CacheLong(),
      variables: {
        headerMenuHandle: 'main-menu', // Adjust to your header menu handle
      },
    }),
    storefront.query(JOY_SHOP_DATA_QUERY, {variables: {}}),
    // Add other queries here, so that they are loaded in parallel
  ]);

  const {shop, localization} = joyApiData;
  const countryCode = localization.language.isoCode;
  const currencyCode = localization.country.currency.isoCode;
  const locale = localization.language.isoCode;

  const joyData = shop.metafield ? JSON.parse(shop.metafield.value) : null;

  return {
    header,
    joyData,
    joyShopData: {
      countryCode,
      locale,
      currencyCode,
    },
  };
}

Once done, create the useJoyLoyalty React hooks inside app/hooks folder with the following content. This hook will take the data from the main loader function, register the global AVADA_JOY variables, and attach more information to the global Shopify variable. The global Shopify variable will by default have the Customer Privacy API registered.

import {useEffect} from 'react';

const joySdk = `https://cdn.shopify.com/extensions/5e2b8eb3-5720-4075-8ce3-fe9c434c4cf2/0.0.0/assets/avada-joy.min.js?v=${new Date().getTime()}`;

/**
 *
 * @param joyData
 * @param joyShopData
 * @param cart
 * @param customer
 * @param publicStoreDomain
 */
export function useJoyLoyalty({
  joyData,
  joyShopData,
  cart,
  customer,
  publicStoreDomain,
}) {
  useEffect(() => {
    try {
      window.AVADA_JOY = window.AVADA_JOY || {};
      window.AVADA_JOY.customer = customer
        ? {
            ...customer,
            first_name: customer.firstName,
            last_name: customer.lastName,
            id: parseFloat(customer.id.replace('gid://shopify/Customer/', '')),
          }
        : {email: null, first_name: null, last_name: null, id: null};
      window.AVADA_JOY = {
        ...window.AVADA_JOY,
        placeOrder: {
          ...joyData.placeOrder,
        },
      };
      window.AVADA_JOY = {
        ...window.AVADA_JOY,
        tierSettings: {
          ...joyData.tierSettings,
        },
      };
      window.AVADA_JOY = {
        ...window.AVADA_JOY,
        allTiers: joyData.allTiers,
      };
      window.AVADA_JOY = {
        ...window.AVADA_JOY,
        pointCalculator: joyData.pointCalculator,
      };
      window.AVADA_JOY.account_enabled = 'true';
      window.AVADA_JOY.login_url = '/account/login';
      window.AVADA_JOY.register_url = '/account/register';
      window.AVADA_JOY.cartProducts =
        cart?.lines?.edges?.map(({node}) => node) || [];
      window.AVADA_JOY.shopId = joyData.shopId;

      window.Shopify = window.Shopify || {};
      window.Shopify.currency = window.Shopify.currency || {
        active: joyShopData?.currencyCode,
        rate: '1.0',
      };
      window.Shopify.locale = joyShopData?.locale?.toLowerCase() || null;
      window.Shopify.country = joyShopData?.countryCode || null;
      window.Shopify.shop = publicStoreDomain || null;

      loadScript({
        id: 'avada-joy-script',
        url: joySdk,
      });
    } catch (e) {
      console.log('Cannot initialize Joy Loyalty', e);
    }
  }, []);
}

/**
 *
 * @param {Object} options
 * @param {string | null} [options.id=null]
 * @param {string | null} [options.url=null]
 */
function loadScript({id = null, url = null} = {}) {
  const hasScript = document.querySelector(`#${id}`);
  if (!hasScript) {
    const script = document.createElement('script');
    script.id = id;
    script.setAttribute('src', url);
    script.async = true;
    document.body.appendChild(script);
  }
}

Once done, invoke the hook inside the root.jsx file (just pay attention to the hook call, you site content may look different).

/**
 * @param {{children?: React.ReactNode}}
 */
export function Layout({children}) {
  const nonce = useNonce();
  /** @type {RootLoader} */
  const data = useRouteLoaderData('root');
  useJoyLoyalty(data);

  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width,initial-scale=1" />
        <Meta />
        <Links />
      </head>
      <body>
        {data ? (
          <Analytics.Provider
            cart={data.cart}
            shop={data.shop}
            consent={data.consent}
          >
            <PageLayout {...data}>{children}</PageLayout>
          </Analytics.Provider>
        ) : (
          children
        )}
        <ScrollRestoration nonce={nonce} />
        <Scripts nonce={nonce} />
      </body>
    </html>
  );
}

Test the widget

After these are implemented, you will see the widget display on your custom storefront according to your settings. However, you can compare the widgets on the custom storefront and the Online Store channel to make sure both are working properly. With the widget, you can earn points, redeem points, and use coupons seamlessly on your store.

Check Content Security Policy

If you cannot see our widget running because of the Content Security Policy, you may need to go to app/entry.server.jsx to update your policy to whitelist our files and domains.

Customer accounts

By default, the Hydrogen template will not have the Customer Account API enabled. You will need to enable them in the Hydrogen sales channel setting. However, if you are not on Shopify Plus or using a development store, you cannot enable this setting to test the Joy Loyalty before implementing these Customer Accounts.

There is a workaround for this. You can assign the customer data to the AVADA_JOY for testing on your store. Use this from your corresponding customer account on your store.

   window.AVADA_JOY.customer = {
        default_address: null,
        email: 'mytesting@gmail.com',
        first_name: null,
        id: 8502697394488, // your testing Shopify customer ID
        last_name: null,
      };

More from here

Our app has other features such as Joy Point Calculator, Joy Rewards Page, etc. If you want to build your own Rewards page using Hydrogen, we will have another post showing how to implement this Rewards page using our Public API and Javascript SDK. We hope that this guide provides you with the first step on how to integrate Joy Loyalty Program widget into your Headless Commerce store.

Last updated