Skip to main content

Lead capture on Hydrogen storefronts

Lead capture signs buyers in through routes that your online store serves. A Hydrogen storefront doesn't serve those routes, so on a Hydrogen page you tell the Shop SDK where your online store is with the storefrontOrigin attribute. The SDK then runs sign-in through the online store and brings the buyer back to your storefront.


In this guide, you'll learn how to do the following tasks:

  • Point lead capture at your online store with storefrontOrigin.
  • Allow your online store and Shop in your storefront's Content Security Policy.
  • Apply the buyer's discount to your Hydrogen cart.
  • Handle the errors that lead capture reports on Hydrogen storefronts.

  • A working lead capture integration that uses a button. Set buttonType to anything other than 'none', which is the default. On Hydrogen storefronts, lead capture signs buyers in through a sign-in window on desktop and a redirect on mobile. It doesn't support the embedded flow that 'none' uses.
  • A Hydrogen storefront that's deployed to Oxygen and served on a custom domain that's connected to it in the Hydrogen channel, such as example.com. Shopify trusts that domain for sign-in, so you don't need to register it. Lead capture doesn't support other headless storefronts.
  • An online store domain on the same site as your storefront, such as checkout.example.com for a storefront at example.com. The buyer's online store session cookie is sent only between same-site origins, so a *.myshopify.com domain doesn't work. For the same reason, test on your custom domain, not on an Oxygen preview URL.

Anchor to Step 1: Set storefrontOrigin and returnToStep 1: Set storefrontOrigin and returnTo

Pass your online store's origin as storefrontOrigin when you create the lead capture instance. It must be an https origin with no path, query, or hash. This is usually your checkout domain, the PUBLIC_CHECKOUT_DOMAIN value in your Hydrogen environment variables.

If you build an app that adds lead capture to many stores, then you need each store's online store origin, because the SDK can't detect it.

Also set returnTo to 'same-page'. On mobile, sign-in redirects the buyer to your online store and back. The default, 'index', brings the buyer back to your storefront's home page. With 'same-page', they return to the page where they started, so lead capture can finish sign-in there and call your handlers:

JavaScript

const leadCapture = await sdk.create('lead-capture', {
attributes: {
buttonType: 'save',
returnTo: 'same-page',
storefrontOrigin: 'https://checkout.example.com',
},
onComplete(event) {
console.log('Lead captured:', event.email);
},
onError(event) {
console.error('Lead capture error:', event.code, event.message);
},
});

document.querySelector('#shop-container').appendChild(leadCapture.element);

With storefrontOrigin set, the SDK loads Shop's sign-in from your online store and sends its requests there with the buyer's session.

If the value isn't a valid origin, then the SDK ignores it, logs a console warning that explains why, and uses your page's origin instead. Lead capture then reports the storefront_origin_required error.


Anchor to Step 2: Update your Content Security PolicyStep 2: Update your Content Security Policy

If your storefront sets a Content Security Policy, then allow your online store and Shop:

DirectiveAdd
frame-srcYour storefrontOrigin (for example, https://checkout.example.com) and https://shop.app
connect-srcYour storefrontOrigin, https://monorail-edge.shopifysvc.com, and https://otlp-http-production.shopifysvc.com
script-srchttps://cdn.shopify.com

Without frame-src, Shop can't recognize returning buyers. Without connect-src, the SDK can't finish sign-in, and Shopify can't see errors from your storefront.

If you build the policy with Hydrogen's createContentSecurityPolicy, then its shop.checkoutDomain option already adds your checkout domain to connect-src, and its defaults allow Monorail and https://cdn.shopify.com. Its default policy doesn't allow frames from your online store or Shop, so add them to frameSrc, and add the OpenTelemetry host to connectSrc:

JavaScript

const checkoutDomain = env.PUBLIC_CHECKOUT_DOMAIN;

const {nonce, header, NonceProvider} = createContentSecurityPolicy({
shop: {
checkoutDomain,
storeDomain: env.PUBLIC_STORE_DOMAIN,
},
frameSrc: ["'self'", `https://${checkoutDomain}`, 'https://shop.app'],
connectSrc: ['https://otlp-http-production.shopifysvc.com'],
});

Setting frameSrc replaces the default policy for frames, so include any other frame sources that your storefront already uses.


Anchor to Step 3: Apply the discount to your cartStep 3: Apply the discount to your cart

On an online store page, the SDK applies the discount you return from onAuthenticate to the buyer's online store cart. A Hydrogen storefront has its own cart, so the SDK skips that step. The SDK still saves the discount to the buyer's Shop account. If saving it fails, then lead capture reports a no_discount_received error and still completes sign-in, because you apply the code yourself.

Apply the code to your Hydrogen cart, for example with the Storefront API cartDiscountCodesUpdate mutation:

JavaScript

const leadCapture = await sdk.create('lead-capture', {
attributes: {
buttonType: 'save',
returnTo: 'same-page',
storefrontOrigin: 'https://checkout.example.com',
},
async onAuthenticate() {
const discount = {code: 'WELCOME10', gid: 'gid://shopify/DiscountCodeNode/123'};
await applyDiscountCodeToCart(discount.code);
return {discount};
},
});

Lead capture reports these errors on Hydrogen storefronts:

CodeWhen it happensWhat lead capture does
storefront_origin_requiredThe page isn't served by the online store, and storefrontOrigin isn't set or isn't a valid origin.Renders nothing, instead of a button that leads to an error page.
storefront_origin_unsupported_flowstorefrontOrigin is set to another origin, and buttonType is 'none'.Still renders the embedded flow, which isn't supported on Hydrogen storefronts. Set buttonType to show a button.
no_discount_receivedThe SDK couldn't save the discount from onAuthenticate to the buyer's Shop account.Completes sign-in.

Each error reaches the feature's onError and the SDK-level onError passed to initialize, including a handler that you add or replace later with update:

JavaScript

const sdk = window.ShopSDK.initialize({
apiKey: 'YOUR_CLIENT_ID',
onError(event) {
if (event.code === 'storefront_origin_required') {
// Set storefrontOrigin to the https origin of your online store.
}
if (event.code === 'storefront_origin_unsupported_flow') {
// Set buttonType to anything other than 'none'.
}
},
});


Was this page helpful?