Loyalty widget

Add the Joy floating loyalty widget to your Hydrogen storefront and connect it to your cart so redeemed rewards apply automatically.

Complete Install manually first — the widget needs the token route and root loader data.

Overview

<JoyWidget/> renders the floating loyalty launcher and panel on every page. Guests see your programs and a sign-in button; logged-in customers see their points, tier and rewards, and can redeem right from the panel.

Add the widget

Render <JoyWidget/> once, in your root layout, outside <main>:

import {JoyWidget} from '@joy-loyalty/hydrogen';
import {useFetcher, useRevalidator, useRouteLoaderData} from 'react-router';
 
export function Layout({children}: {children?: React.ReactNode}) {
  const data = useRouteLoaderData<typeof loader>('root');
  const joyCartFetcher = useFetcher();
  const joyRevalidator = useRevalidator();
  const joyCartRoute = usePrefixPathWithLocale('/cart'); // your locale helper
 
  return (
    // ...<html>, <body>
    <>
      <main>{children}</main>
      <JoyWidget
        apiUrl={data?.joyApiUrl ?? null}
        shopDomain={data?.joyShopDomain ?? undefined}
        assetUrl={data?.joyAssetUrl ?? undefined}
        initialData={data?.joy ?? null}
        locale={locale.language?.toLowerCase()}
        currency={locale.currency}
        country={locale.country}
        cartFetcher={joyCartFetcher}
        cartRevalidate={joyRevalidator.revalidate}
        cartRoute={joyCartRoute}
        onOpenCartDrawer={openCartDrawer}
      />
    </>
  );
}

Props

PropDefaultWhat it does
apiUrlnullThe Joy headless API URL from your root loader. null turns off live data.
shopDomainFrom Joy dataYour myshopify domain.
initialDatanullThe joy object from your root loader — lets the widget paint on the first load.
assetUrlJoy CDNOverrides where the widget code loads from. Leave unset in production.
localeStore's primary languageLanguage for Joy texts and number formatting. Pass it lowercase.
currencyCart currency, then USDCurrency for product prices in the widget.
countryUSMarket used for free-gift stock.
tokenRoute/api/joy-tokenChange only if you moved the token route.
loginUrl/accountWhere the Sign in button goes.
loyaltyPageUrl/pages/rewardsWhere View rewards goes. Point it at your Loyalty Page.
pathnamewindow.location.pathnameCurrent route pathname (useLocation().pathname) for "Recently viewed" tracking.
preload'idle'When to load the widget code — see below.
cart—Current cart, or a function returning it.
cartFetcher—useFetcher() — needed for cart actions.
cartRevalidate—useRevalidator().revalidate — needed for cart actions.
cartRoute/cartYour cart action route, including the locale prefix.
onOpenCartDrawer—Opens your cart drawer after a reward is applied.
positionJoy admin setting'bottom-right' or 'bottom-left'. Set only to override Joy admin.
accentColorJoy admin settingLauncher color. Set only to override Joy admin.
launcherLabelJoy admin settingLauncher text. Set only to override Joy admin.
panelHeading, pointsLabel, viewRewardsLabel, loginLabel'Your rewards', 'points', 'View rewards', 'Log in'Texts for the simple fallback panel, shown only if the full widget can't load.

Branding comes from Joy admin

Colors, layout, texts and translations come from the widget editor in Joy admin — there is no theme or appearance prop. Leave position, accentColor and launcherLabel unset so changes you make in Joy admin show up on your storefront without a redeploy.

Connect the widget to your cart

When a customer redeems a reward, the widget can apply the coupon to the cart or add a free gift. For that it needs:

  • cartFetcher and cartRevalidate together. Without both, the widget still shows the coupon code but doesn't apply it.
  • A locale-aware cartRoute. On a multi-market store, pass the prefixed path, e.g. /fr-ca/cart. Your cart route action must accept the standard Hydrogen CartForm actions.
  • onOpenCartDrawer (optional) to open your drawer after the cart changes. If you wrap your pages in <JoyProvider onOpenCartDrawer={...}>, the widget uses that handler too.

Choose when the widget loads

preloadBehaviorUse when
'idle' (default)Loads the widget code once the page is idle.Most stores — matches the Online Store theme experience.
'interaction'Loads on the first click or hover on the launcher.You want the best page-speed scores and accept a slightly slower first open.
'eager'Loads immediately.The widget is central to the page.

Sign in and sign out

  • Guests see a Sign in button that goes to loginUrl. After signing in through your Customer Account API login, the widget shows the customer's balance.
  • Signing out revokes the Joy token on that device only; other devices stay signed in.
  • If you exclude a customer from your loyalty program in Joy admin, they are signed out of the widget on all devices.

Related