---
title: lead-capture
description: >-
  The lead-capture feature enables merchants to capture leads on their
  storefront by recognizing returning Shop users and facilitating email
  collection.
source_url:
  html: 'https://shopify.dev/docs/api/shop-sdk/reference/lead-capture'
  md: 'https://shopify.dev/docs/api/shop-sdk/reference/lead-capture.md'
---

# lead-capture

The `lead-capture` feature enables merchants to capture leads on their storefront by recognizing returning [Shop](https://shop.app) users and facilitating email collection.

It supports two main flows:

* **Discount flow** — Offer a discount code in exchange for an email, using the `onAuthenticate` callback.
* **Default flow** — Standard email capture without a discount incentive.

The feature detects whether the current visitor is a known Shop user and can display a call-to-action button (for example, "Continue with Shop") or integrate inline with an existing email input field.

***

## Usage

Create a lead-capture instance via the SDK:

```javascript
const sdk = window.ShopSDK.initialize({
  apiKey: 'YOUR_STOREFRONT_API_KEY',
});


const leadCapture = await sdk.create('lead-capture', {
  attributes: {
    emailInputSelector: '#email',
  },
  onComplete(event) {
    console.log('Lead captured:', event.email);
  },
});


document.querySelector('#mount').appendChild(leadCapture.element);
```

You can also eagerly preload the feature loader before calling `create`:

```javascript
const sdk = window.ShopSDK.initialize({
  apiKey: 'YOUR_STOREFRONT_API_KEY',
  features: { 'lead-capture': true },
});
```

#### LeadCaptureConfig

Config accepted by `sdk.create('lead-capture', config)`.

Derived from `LeadCaptureRegistrationSpec`: includes all component attributes, all event handlers, plus SDK-specific fields like `appearance`.

* **appearance**

  **AppearanceConfig**

  Component-level appearance overrides. Variables set here take precedence over those set in `initialize` or `update()`.

* **attributes**

  **{ apiKey?: string; clientId?: string; devMode?: boolean; emailInputSelector?: string; buttonType?: LeadCaptureButtonType; buttonLayout?: LeadCaptureButtonLayout; storefrontOrigin?: string; uxMode?: "redirect" | "iframe" | "windoid"; scope?: string; disclosureScope?: string; disclosureText?: string; disclosureTitle?: string; phoneCapture?: boolean; phoneCaptureDisclosureText?: string; }**

  Component attributes. These map to HTML attributes on the underlying `<shop-lead-capture>` element.

* **onAuthenticate**

  **() => Promise<{ discount: LeadCaptureDiscount; }>**

  Fired during the discount flow to retrieve the discount to offer.

* **onBeforeAuthenticate**

  **() => void | Promise\<void>**

  Fired before authentication begins. Can be async.

* **onComplete**

  **(event: LeadCaptureCompleteEvent) => void**

  Fired when the lead capture flow completes successfully.

* **onConfirmSuccess**

  **() => void**

  Fired after the user confirms a successful action.

* **onError**

  **(event: LeadCaptureErrorEvent) => void**

  Fired when an error occurs during the flow.

* **onGetEmailInput**

  **() => HTMLInputElement**

  Fired to retrieve the email input element (alternative to emailInputSelector).

* **onLoad**

  **(event: LeadCaptureLoadEvent) => void**

  Fired when the component loads and user recognition completes.

* **onReady**

  **(event: ReadyEvent) => void**

  SDK-level ready handler (fires when the element's loader resolves).

* **onRestart**

  **() => void**

  Fired when the user restarts the flow.

### AppearanceConfig

Appearance configuration for styling Shop SDK components. Set at the instance level in \`initialize()\` or per-feature in \`create()\`.

* preferredLoginExperience

  Preferred UX for the login experience: \`'redirect'\`, \`'popup'\`, or \`null\` for the default.

  ```ts
  'redirect' | 'popup' | null
  ```

* variables

  CSS custom property overrides.

  ```ts
  AppearanceVariables
  ```

### AppearanceVariables

CSS custom properties that control styling of Shop SDK components. All properties are optional — only set ones will be applied.

* \--buttons-radius

  Border radius for buttons (e.g. \`'12px'\`).

  ```ts
  string
  ```

* \--font-paragraph--line-height

  Line height for paragraph text (e.g. \`'22px'\`).

  ```ts
  string
  ```

* \--font-paragraph--size

  Font size for paragraph text (e.g. \`'16px'\`).

  ```ts
  string
  ```

* \--shop-pay-button-border-radius

  Border radius of the Shop Pay button (e.g. \`'4px'\`, \`'999px'\`).

  ```ts
  string
  ```

* \--shop-pay-button-height

  Height of the Shop Pay button (e.g. \`'48px'\`).

  ```ts
  string
  ```

* \--shop-pay-button-width

  Width of the Shop Pay button (e.g. \`'260px'\`, \`'100%'\`).

  ```ts
  string
  ```

* \--x-spacing-base

  Base spacing unit (e.g. \`'14px'\`).

  ```ts
  string
  ```

* \[variable: \`--${string}\`]

  ```ts
  string | undefined
  ```

### LeadCaptureButtonType

The type of call-to-action button shown when a Shop user is recognized. - \`'none'\` — No button; uses inline email-based flow instead. - \`'claim'\` — Shows “Claim with Shop” button. - \`'continue'\` — Shows “Continue with Shop” button. - \`'favorite'\` — Shows “Favorite with Shop” button. - \`'notify'\` — Shows “Notify me with Shop” button. - \`'save'\` — Shows “Save with Shop” button.

```ts
'none' | 'claim' | 'continue' | 'favorite' | 'notify' | 'save'
```

### LeadCaptureButtonLayout

Layout style for the lead capture button. - \`'or'\` — Shows an “or” separator between the button and the form. - \`'standalone'\` — Shows the button without any separator.

```ts
'or' | 'standalone'
```

### LeadCaptureDiscount

A discount to apply after successful lead capture.

* code

  The discount code string.

  ```ts
  string
  ```

* gid

  The Shopify global ID of the discount, if available.

  ```ts
  string
  ```

### LeadCaptureCompleteEvent

Event payload dispatched when the lead capture flow completes successfully.

* consentedScopes

  A space-separated list of OAuth scopes the user consented to.

  ```ts
  string
  ```

* customerAccessToken

  A customer access token issued after authentication, if available.

  ```ts
  string
  ```

* customerAccessTokenExpiresAt

  ISO 8601 timestamp of when the customer access token expires.

  ```ts
  string
  ```

* customerUpdateErrors

  Error messages from the customer update mutation, if any.

  ```ts
  string
  ```

* disclosureAgreed

  Whether the user agreed to the disclosure presented during the flow.

  ```ts
  boolean
  ```

* email

  The email address captured from the user.

  ```ts
  string
  ```

* emailVerified

  Whether the user's email has been verified.

  ```ts
  boolean
  ```

* phoneShareConsent

  Whether the user consented to share their phone number.

  ```ts
  boolean
  ```

* shopConsentToken

  A token that can be exchanged in the Shop Partners API to receive access for a user in the Shop Users API

  ```ts
  string
  ```

* signedIn

  Whether the user signed in during the flow (as opposed to only providing their email).

  ```ts
  boolean
  ```

### LeadCaptureErrorEvent

Event payload dispatched when an error occurs during lead capture.

* code

  A machine-readable error code.

  ```ts
  string
  ```

* email

  The email address associated with the error, if available.

  ```ts
  string
  ```

* message

  A human-readable error message.

  ```ts
  string
  ```

### LeadCaptureLoadEvent

Event payload dispatched when the lead capture component finishes loading.

* userFound

  Whether a recognized Shop user was detected for the current browser session.

  ```ts
  boolean
  ```

### ReadyEvent

Event payload passed to the \`onReady\` handler when a feature element finishes loading.

* detail

  Underlying \`CustomEvent.detail\` from the element's \`'loaded'\` event, when available. Shape is feature-specific — e.g. for lead-capture this includes fields like \`userFound\`, \`loginTitle\`, etc. emitted by \`useAuthorizeEventListener\`.

  ```ts
  unknown
  ```

* elementTagName

  Tag name of the element that became ready, e.g. \`shop-lead-capture\`.

  ```ts
  string
  ```

#### LeadCaptureInstance

Runtime instance returned by `sdk.create('lead-capture')`.

Exposes:

* All component methods from the registration spec
* `setAttribute` for updating attributes after creation
* Event subscription methods for each event handler
* `onReady` with late-subscriber semantics
* `element` for DOM insertion
* `destroy` for cleanup

- **destroy**

  **() => void**

  **required**

  Tear down listeners and remove the element from the DOM if attached.

- **element**

  **HTMLElement**

  **required**

  The underlying custom element. The consumer is responsible for inserting it into the DOM.

- **notifyEmailFieldShown**

  **() => void**

  **required**

  Notify the component that the email field has been shown.

- **onAuthenticate**

  **(handler: () => Promise<{ discount: LeadCaptureDiscount; }>) => void**

  **required**

  Register an `onAuthenticate` handler.

- **onBeforeAuthenticate**

  **(handler: () => void | Promise\<void>) => void**

  **required**

  Register an `onBeforeAuthenticate` handler.

- **onComplete**

  **(handler: (event: LeadCaptureCompleteEvent) => void) => void**

  **required**

  Register an `onComplete` handler.

- **onConfirmSuccess**

  **(handler: () => void) => void**

  **required**

  Register an `onConfirmSuccess` handler.

- **onError**

  **(handler: (event: LeadCaptureErrorEvent) => void) => void**

  **required**

  Register an `onError` handler.

- **onLoad**

  **(handler: (event: LeadCaptureLoadEvent) => void) => void**

  **required**

  Register an `onLoad` handler.

- **onReady**

  **(handler: (event: ReadyEvent) => void) => void**

  **required**

  Register an `onReady` handler. Fires immediately if already ready.

- **onRestart**

  **(handler: () => void) => void**

  **required**

  Register an `onRestart` handler.

- **setAttribute**

  **(attrs: Partial<{ apiKey?: string; clientId?: string; devMode?: boolean; emailInputSelector?: string; buttonType?: LeadCaptureButtonType; buttonLayout?: LeadCaptureButtonLayout; storefrontOrigin?: string; uxMode?: "redirect" | "iframe" | "windoid"; scope?: string; disclosureScope?: string; disclosureText?: string; disclosureTitle?: string; phoneCapture?: boolean; phoneCaptureDisclosureText?: string; }>) => void**

  **required**

  Update one or more attributes on the underlying element. Accepts the same attribute keys as `config.attributes`.

- **start**

  **(email?: string) => void**

  **required**

  Trigger the lead-capture flow, optionally with a pre-filled email.

#### LeadCaptureCompleteEvent

Event payload dispatched when the lead capture flow completes successfully.

* **email**

  **string**

  **required**

  The email address captured from the user.

* **signedIn**

  **boolean**

  **required**

  Whether the user signed in during the flow (as opposed to only providing their email).

* **consentedScopes**

  **string**

  A space-separated list of OAuth scopes the user consented to.

* **customerAccessToken**

  **string**

  A customer access token issued after authentication, if available.

* **customerAccessTokenExpiresAt**

  **string**

  ISO 8601 timestamp of when the customer access token expires.

* **customerUpdateErrors**

  **string**

  Error messages from the customer update mutation, if any.

* **disclosureAgreed**

  **boolean**

  Whether the user agreed to the disclosure presented during the flow.

* **emailVerified**

  **boolean**

  Whether the user's email has been verified.

* **phoneShareConsent**

  **boolean**

  Whether the user consented to share their phone number.

* **shopConsentToken**

  **string**

  A token that can be exchanged in the Shop Partners API to receive access for a user in the Shop Users API

#### LeadCaptureErrorEvent

Event payload dispatched when an error occurs during lead capture.

* **code**

  **string**

  **required**

  A machine-readable error code.

* **message**

  **string**

  **required**

  A human-readable error message.

* **email**

  **string**

  The email address associated with the error, if available.

#### LeadCaptureLoadEvent

Event payload dispatched when the lead capture component finishes loading.

* **userFound**

  **boolean**

  **required**

  Whether a recognized Shop user was detected for the current browser session.

Examples

### Examples

* ####

  ##### Description

  Create a lead-capture instance with an email input and handle the complete event.

  ##### HTML

  ```html
  <script type="module">
    import 'https://cdn.shopify.com/shopifycloud/shop-js/modules/v2/loader.sdk.esm.js';

    const sdk = window.ShopSDK.initialize({
      apiKey: 'YOUR_STOREFRONT_API_KEY',
      locale: 'en',
    });

    const leadCapture = await sdk.create('lead-capture', {
      attributes: {
        emailInputSelector: '#email',
      },
      onComplete(event) {
        console.log('Lead captured:', event.email);
      },
    });

    document.querySelector('#lead-capture-mount').appendChild(leadCapture.element);
  </script>

  <form id="lead-capture-form">
    <label for="email">Email address</label>
    <input type="email" id="email" name="email" placeholder="Enter your email" />
  </form>

  <div id="lead-capture-mount"></div>
  ```

* ####

  ##### Description

  Offer a discount code to users who provide their email via the \`onAuthenticate\` callback.

  ##### HTML

  ```html
  <script type="module">
    import 'https://cdn.shopify.com/shopifycloud/shop-js/modules/v2/loader.sdk.esm.js';

    const sdk = window.ShopSDK.initialize({
      apiKey: 'YOUR_STOREFRONT_API_KEY',
      locale: 'en',
      onError(event) {
        console.error('[ShopSDK]', event.message, event.cause);
      },
    });

    const leadCapture = await sdk.create('lead-capture', {
      attributes: {
        emailInputSelector: '#newsletter-email',
        flow: 'discount',
      },
      onAuthenticate: async () => {
        return { discount: { code: 'WELCOME10' } };
      },
      onComplete(event) {
        console.log('Lead captured:', event.email);
        console.log('Signed in:', event.signedIn);
      },
      onLoad(event) {
        if (event.userFound) {
          console.log('Recognized Shop user detected');
        }
      },
    });

    document.querySelector('#lead-capture-mount').appendChild(leadCapture.element);
  </script>

  <form id="newsletter-form">
    <label for="newsletter-email">Get 10% off your first order</label>
    <input
      type="email"
      id="newsletter-email"
      name="email"
      placeholder="Enter your email"
    />
    <button type="submit">Subscribe</button>
  </form>

  <div id="lead-capture-mount"></div>
  ```

* ####

  ##### Description

  Show a "Continue with Shop" button for recognized users instead of an inline email input.

  ##### HTML

  ```html
  <script type="module">
    import 'https://cdn.shopify.com/shopifycloud/shop-js/modules/v2/loader.sdk.esm.js';

    const sdk = window.ShopSDK.initialize({
      apiKey: 'YOUR_STOREFRONT_API_KEY',
      locale: 'en',
    });

    const leadCapture = await sdk.create('lead-capture', {
      attributes: {
        buttonType: 'continue',
        buttonLayout: 'or',
      },
      onComplete(event) {
        console.log('Lead captured:', event.email);
      },
      onLoad(event) {
        console.log('User found:', event.userFound);
      },
      onError(event) {
        console.error('Error:', event.code, event.message);
      },
    });

    document.querySelector('#lead-capture-mount').appendChild(leadCapture.element);
  </script>

  <div id="lead-capture-mount"></div>
  ```

***

## Related

[Overview - Shop SDK](https://shopify.dev/docs/api/shop-sdk/reference/shop-sdk)

***
