@joy-loyalty/weaverse-hydrogen
v0.2.0
Published
Joy Loyalty blocks for Weaverse Hydrogen storefronts (drag-and-drop sections backed by the Joy Headless API)
Maintainers
Readme
@joy-loyalty/weaverse-hydrogen
Joy Loyalty blocks for Weaverse Hydrogen storefronts. Adds a Joy - Loyalty Page block to Weaverse Studio that renders the same Loyalty Page V2 core the Joy Liquid storefront runs, so styling is done once, in the Joy admin.
Built on @joy-loyalty/hydrogen and @joy-loyalty/sdk. Version 0.2.0 replaced the earlier nine React blocks with this single block; see CHANGELOG.md before upgrading.
ESM-only. Peers:
@weaverse/hydrogen^5.19.2,react18/19,react-router^7,@shopify/hydrogen>= 2025.5 (optional).
Prerequisites
- A Joy Loyalty store on the Ultimate plan (the Headless API is plan-gated).
- A Weaverse Hydrogen storefront with Customer Account API login.
- The headless API secret from your Joy admin.
Install
npm install @joy-loyalty/weaverse-hydrogen @joy-loyalty/hydrogen @joy-loyalty/sdkEnvironment variables
| Variable | Where | Notes |
|---|---|---|
| JOY_API_URL | server | e.g. https://api.joy.so/app/api/v1/headless. Not a credential. |
| JOY_SHOP_DOMAIN | server | The shop's *.myshopify.com domain. |
| JOY_API_SECRET | server only | The headless API secret from your Joy admin. |
| JOY_WIDGET_ASSET_URL | optional | Overrides the widget-core origin (default https://static.joy.so). |
Warning: server-only secret.
JOY_API_SECRETmust only be read on the server (Oxygen loaders and resource routes). Never prefix itPUBLIC_orVITE_, never return it from a loader, and never import code that reads it into a client bundle.Note: the secret will be rotated to a dedicated headless secret before general availability. The variable name stays
JOY_API_SECRET.
Setup
1. Register the block
// app/weaverse/components.ts
import {joyComponents} from '@joy-loyalty/weaverse-hydrogen';
export const components: HydrogenComponent[] = [...pilotComponents, ...joyComponents];2. Token route
// app/routes/api.joy-token.ts
export {loader} from '@joy-loyalty/weaverse-hydrogen/routes/joy-token';3. Optional place-order scope route (only for place_order programs restricted to products/collections)
// app/routes/api.joy-place-order-scope.ts
export {loader} from '@joy-loyalty/weaverse-hydrogen/routes/joy-place-order-scope';4. Root loader and provider. The block reads the root loader's joy key. Build it with getJoyInitialData from @joy-loyalty/hydrogen/server (see the @joy-loyalty/hydrogen README) and, if your app does not wire cart state otherwise, wrap your content in JoyProvider.
5. CSP — use the directives from @joy-loyalty/hydrogen/csp:
import {JOY_CSP_DIRECTIVES} from '@joy-loyalty/hydrogen/csp';
createContentSecurityPolicy({
scriptSrc: ['self', ...JOY_CSP_DIRECTIVES.scriptSrc],
connectSrc: ['self', ...JOY_CSP_DIRECTIVES.connectSrc],
styleSrc: ['self', ...JOY_CSP_DIRECTIVES.styleSrc],
imgSrc: ['self', ...JOY_CSP_DIRECTIVES.imgSrc]
});For a custom asset/API host use joyCspDirectives({apiUrl, assetUrl}).
Caching
Use isJoyBlockDataPersonalized and JOY_PERSONALIZED_CACHE_CONTROL (exported here) to set Cache-Control: private on pages whose Joy data is personalized.
Exports
joyComponents, JoyLoyaltyPage, JoyLoyaltyWidget, JoyProvider, useJoyConfig, useJoyCustomer, getJoyToken, clearJoyToken, DEFAULT_TOKEN_ROUTE, isJoyBlockDataPersonalized, JOY_PERSONALIZED_CACHE_CONTROL. Subpaths: /routes/joy-token, /routes/joy-place-order-scope. Note joyComponents contains only the Loyalty Page block; JoyLoyaltyWidget is exported for custom registration.
Security notes
- The customer JWT is kept in
sessionStorage(about 1 hour TTL), so XSS on your storefront can read it. Keep a strict CSP. - The widget core is loaded from
static.joy.so(override withJOY_WIDGET_ASSET_URL); allow-list only trusted origins. - The token route answers
Cache-Control: private, no-store.
License
Proprietary. Copyright (c) 2026 Avada Group. All rights reserved. Licensed only for use with the Joy Loyalty service under the Joy Loyalty Terms of Service. See LICENSE.
