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
| Prop | Default | What it does |
|---|---|---|
apiUrl | null | The Joy headless API URL from your root loader. null turns off live data. |
shopDomain | From Joy data | Your myshopify domain. |
initialData | null | The joy object from your root loader — lets the widget paint on the first load. |
assetUrl | Joy CDN | Overrides where the widget code loads from. Leave unset in production. |
locale | Store's primary language | Language for Joy texts and number formatting. Pass it lowercase. |
currency | Cart currency, then USD | Currency for product prices in the widget. |
country | US | Market used for free-gift stock. |
tokenRoute | /api/joy-token | Change only if you moved the token route. |
loginUrl | /account | Where the Sign in button goes. |
loyaltyPageUrl | /pages/rewards | Where View rewards goes. Point it at your Loyalty Page. |
pathname | window.location.pathname | Current 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 | /cart | Your cart action route, including the locale prefix. |
onOpenCartDrawer | — | Opens your cart drawer after a reward is applied. |
position | Joy admin setting | 'bottom-right' or 'bottom-left'. Set only to override Joy admin. |
accentColor | Joy admin setting | Launcher color. Set only to override Joy admin. |
launcherLabel | Joy admin setting | Launcher 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:
cartFetcherandcartRevalidatetogether. 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 HydrogenCartFormactions. 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
preload | Behavior | Use 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.