Hydrogen is a React Router app deployed to Shopify’s Oxygen edge runtime (a Workers-style runtime, not Node.js). This guide was validated against Hydrogen 2026.4 and React Router 7. The exact APIs may differ slightly between Hydrogen versions — check Shopify’s documentation if something doesn’t match your setup.
Choose how to integrate
- JavaScript SDK (recommended for Hydrogen) — Build a fully custom loyalty UI as React components. Because the JavaScript SDK refreshes customer state via its own methods, it works naturally with Hydrogen’s client-side navigation and doesn’t require full page reloads.
- Smile UI — Add Smile’s pre-built panel and launcher with minimal code. Works on Hydrogen, but the panel relies on full page reloads to refresh customer state, so you’ll need to trigger reloads manually (see below).
- Both — Use Smile UI for the panel and the JavaScript SDK for custom flows by passing
includeSdk: truewhen initializing Smile UI.
Required setup
Regardless of which integration approach you choose, you’ll need to complete the following required setup.1. Define your environment variables
The rest of this guide assumes two environment variables are available to your loaders throughcontext.env:
SMILE_PUBLISHABLE_KEY— identifies your Smile account and is safe to expose in client-side code. The help docs cover where to find your publishable key.SMILE_SIGNING_KEY— signs the customer tokens you’ll generate in the next step. The help docs cover how to create a signing key.
.env file, and set them as environment variables on your Hydrogen storefront so they’re available to deployed environments.
2. Generate the customer token
Smile uses customer tokens to identify the currently logged-in user, and they must be generated on the backend to ensure your Smile signing key is never exposed. In Hydrogen, loaders and actions run server-side, which is the right place to generate the customer token. Generating a customer token involves two steps: using the Customer Account API to identify the logged-in customer, then signing a JWT that contains the numeric portion of their Shopify customer ID.1
Get the logged-in customer's ID
app/root.jsx
2
Generate a signed JWT
The Node Things to consider:
jsonwebtoken library isn’t available on Oxygen, so you must use a runtime-compatible JWT library such as jose (which uses Web Crypto), or Web Crypto’s crypto.subtle directly:app/lib/smile.server.js
- The
submust be the numeric Shopify customer ID (the tail of thegid://shopify/Customer/…GID), not Smile’s internal customer ID — the two look alike, but a token signed with the wrong one is rejected. - Choose an expiry that covers a full browsing session. Smile verifies the token’s expiry on every API call, and your root loader only generates a fresh token on a full page load — a short-lived token causes loyalty requests to start failing for customers who keep a page open longer than the expiry. One hour matches the lifetime Shopify uses for its own Customer Account API access tokens.
3. Allowlist Smile in your CSP
Hydrogen applies a strict Content Security Policy by default, which will block Smile until you allowlist its origins. Extend the policy inentry.server.jsx, picking the tab that matches how you chose to integrate:
- JavaScript SDK
- Smile UI
- Both
app/entry.server.jsx
Using the JavaScript SDK
This is the recommended approach for Hydrogen, since it gives you complete control to create a fully custom React-based loyalty UI and doesn’t rely on full page reloads.1
Include the JavaScript SDK
Load Smile’s JavaScript SDK with Hydrogen’s
Script component, which automatically applies the CSP nonce. Always load the JavaScript SDK from Smile’s CDN rather than bundling it.app/root.jsx
2
Initialize the JavaScript SDK
Create a new component file that loads and initializes the JavaScript SDK. It should wait for the The
smile-js-loaded event then initialize with your publishable key, the customer token from your loader, and any resources to preload.app/components/SmileSdk.jsx
initialized ref guards initialization to run exactly once per page load — React’s StrictMode runs effects twice in development, and this effect re-runs whenever the customer token changes. Session changes after initialization are handled with dedicated methods instead, covered in the login and logout step below.Render <SmileSdk /> once in your root layout, alongside the Script tag, so the JavaScript SDK initializes on every route:app/root.jsx
On Remix-based Hydrogen versions (2025.1 and earlier), import
useRouteLoaderData from @remix-run/react instead — React Router 7 replaced the Remix packages.3
Handle login and logout
Hydrogen’s default Customer Account API login redirects through Shopify’s hosted login page — a full navigation — so when the customer lands back on your storefront, your root loader generates a customer token for the new session and Smile’s JavaScript SDK initializes with the logged-in customer automatically. No extra work needed.If your app changes the customer session without a full navigation, tell the JavaScript SDK directly. The customer token comes from the same place: after your root loader revalidates, read the updated After any action that changes a customer’s points balance or rewards information, call
customerToken from useRouteLoaderData('root') and pass it along:JavaScript
Smile.preload() to pull in the latest data. When calling this method, make sure you pass in the appropriate resource keys for the data you want refreshed.4
Build your loyalty UI
Use the available JavaScript SDK methods to implement key loyalty flows. At a minimum, Smile recommends:
- A list of the available ways to earn points
- A place for customers to see their points balance, VIP tier, and unused rewards
- A way for customers to redeem their points at checkout
Using Smile UI
Use Smile UI if you want loyalty information displayed to customers in Smile’s pre-built panel and launcher, with minimal custom code required.Key considerations
- Page reloads — The panel and launcher rely on full page reloads to detect login/logout and refresh customer state. Because Hydrogen navigates client-side, you must manually trigger a full reload (
window.location.reload()) whenever a customer logs in or out, or after any action that changes their points balance or rewards information (like redeeming for a coupon). CallingSmileUI.initialize()again without a full reload is not sufficient. - Nudges — Nudges are not triggered or visible when Smile UI is added to a Hydrogen storefront.
- Translations — On a Hydrogen storefront, the panel will not auto-detect the customer’s browser language. Instead, loyalty content will always be presented in the language configured for the program in Smile Admin.
Integration instructions
1
Include Smile UI from the CDN
app/root.jsx
2
Initialize Smile UI
Create a new component file that initializes Smile UI with your publishable key and the customer token from your loader. Pass The
includeSdk: true if you also want the JavaScript SDK available.Because the script loads before React hydrates, SmileUI is usually already available by the time your effect runs — check for window.SmileUI first and fall back to the smile-ui-loaded event. Listening for the event alone isn’t enough, since it fires before any effect runs.app/components/SmileUi.jsx
initialized ref in this example guards initialization to run exactly once per page load. If SmileUI.initialize() runs a second time — React’s StrictMode runs effects twice in development, and this effect re-runs whenever the customer token changes — SmileUI.openPanel() silently stops working. Reflect session changes with a full page reload instead of re-initializing.Unlike the JavaScript SDK’s Smile.initialize(), SmileUI.initialize() does not return a Promise, so chaining .then() on it throws. SmileUI.ready() is the Promise-based completion signal.Render <SmileUi /> once in your root layout, alongside the Script tag:app/root.jsx
3
Trigger full reloads after key customer actions
Ensure a full page reload occurs whenever a customer logs in or out, or when an action outside the panel changes their points balance or rewards information. You can call
window.location.reload() or an equivalent for your setup.If you use Hydrogen’s default Customer Account API login, login and logout already happen as full page loads — Shopify’s hosted login page redirects back to your storefront — so Smile UI picks up the new session automatically. You only need to trigger reloads for actions that change customer state without a full navigation. Logged-out visitors have no customer state to refresh, so reloads only matter once a customer is logged in.4
Add links to open the panel
Add menu items or buttons that open the panel to a specific screen using deep links.Because Hydrogen navigates client-side, the
smile_deep_link query parameter and #smile-… anchor mechanisms only take effect on a full page load — they never fire when a customer navigates with React Router’s <Link> component.- To open the panel on the page the customer is already on, use the
data-smile-deep-linkHTML attribute — it works on any anchor, wherever it’s rendered. - To navigate to a different page and then automatically open the panel, force a full navigation with
<Link reloadDocument>or a plain<a>tag.
JSX