Skip to main content
Yes, Smile works on Shopify Hydrogen! Because a Hydrogen storefront is a custom React app backed by Shopify, the same approach used for any headless or single page application applies. You can use the JavaScript SDK to build a fully custom loyalty experience, or drop in Smile UI for a pre-built panel and launcher. This guide covers the three Hydrogen-specific things you’ll need to handle: generating the customer token in a server loader, allowlisting Smile in your Content Security Policy, and accounting for Hydrogen’s client-side navigation.
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: true when 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 through context.env: Add both to your local .env file, and set them as environment variables on your Hydrogen storefront so they’re available to deployed environments.
The signing key is a secret. Keep it in environment variables — never include it in client-side code or commit it to your repository.

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 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
Things to consider:
  • The sub must be the numeric Shopify customer ID (the tail of the gid://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 in entry.server.jsx, picking the tab that matches how you chose to integrate:
app/entry.server.jsx
In every directive Hydrogen doesn’t set a default value for (scriptSrc, imgSrc, and frameSrc above), re-add 'self' and https://cdn.shopify.com (and https://shopify.com for images) yourself. Once a directive is defined, the browser no longer falls back to default-src, so omitting them blocks your own scripts and product images. Hydrogen merges your values into the directives it does set defaults for (connectSrc, styleSrc), so those only need Smile’s origins.

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
Don’t pass waitForHydration to the Script component. It injects the script while React is still hydrating, which fails hydration on every page load — and the JavaScript SDK never loads. The plain <Script> component renders the script tag with the CSP nonce server-side and hydrates cleanly.
2

Initialize the JavaScript SDK

Create a new component file that loads and initializes the JavaScript SDK. It should wait for 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
The 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 customerToken from useRouteLoaderData('root') and pass it along:
JavaScript
After any action that changes a customer’s points balance or rewards information, call 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). Calling SmileUI.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
Don’t pass waitForHydration to the Script component. It injects the script while React is still hydrating, which fails hydration on every page load — and Smile UI never loads. The plain <Script> component renders the script tag with the CSP nonce server-side and hydrates cleanly.
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 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
The 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-link HTML 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