---
gid: DFCDE5B6-4D3A-4E3A-AD43-F601AAA3B2B4
title: Build a pre-purchase product offer checkout extension
description: Learn how to offer customers additional products at checkout that they can add to their order.
---

import Deploy from 'app/views/partials/extensions/deploy.mdx'
import CheckoutUiRequirements from 'app/views/partials/apps/checkout/ui-extensions/requirements.mdx'
import CheckoutUiCreate from 'app/views/partials/apps/checkout/ui-extensions/create.mdx'
import CheckoutUiPreview from 'app/views/partials/apps/checkout/ui-extensions/preview.mdx'
import CheckoutUiReference from 'app/views/partials/apps/checkout/ui-extensions/reference.mdx'

<Repo
  extension="react"
  href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--react"
/>
<Repo
  extension="javascript"
  href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--js"
/>

<Picker name="extension">
  <PickerOption name="react" />
  <PickerOption name="javascript" />
</Picker>

<Overview>

  A pre-purchase product offer is an additional sales opportunity that's displayed to customers before they complete checkout. Pre-purchase product offers can help to increase a store's average order value.

  In this tutorial, you'll use checkout UI extensions to build a pre-purchase upsell offer that prompts the customer to add a product to their order.

  Before you start, consider reviewing our [product offer checkout UI extension UX guidelines](/apps/checkout/product-offers/product-offer-composition).

  <Notice type="shopifyPlus" title="Shopify Plus">
    Checkout UI extensions are available only to [Shopify Plus](https://www.shopify.com/plus) merchants.
  </Notice>

  <video autoPlay muted loop controls>
    <source src="/assets/apps/checkout/product-offer.webm" type="video/webm"/>
    <source src="/assets/apps/checkout/product-offer.mp4" type="video/mp4"/>
  </video>

  ## What you'll learn

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

  - Generate a checkout UI extension that appears in the checkout flow using Shopify CLI.
  - Set up configurations for your Checkout UI extension in the extension TOML file.
  - Query the Storefront API from the extension code to get product data.
  - Use the Checkout UI component library to add new user interface to the checkout.
  - Use the Checkout UI extension API to read and write cart information.

</Overview>

<Requirements>
  <CheckoutUiRequirements />
</Requirements>

<StepSection>
  <Step>

    ## Create a Checkout UI extension

    To create a checkout UI extension, you'll use Shopify CLI, which generates starter code for building your extension.

    <Substep>

      <CodeRef
      extension="react"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--react/blob/main/extensions/product-offer/shopify.extension.toml" />

      <CodeRef
      extension="javascript"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--js/blob/main/extensions/pre-purchase-offer/shopify.extension.toml" />

      <CheckoutUiCreate />

    </Substep>
  </Step>
  <Step>

    ## Set up an extension target

    Set up a target for your checkout UI extension. [Targets](/docs/api/checkout-extensions/checkout#extension-targets) control where your extension renders in the checkout flow.

    <Substep>

    <CodeRef
    extension="react"
    href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--react/blob/main/extensions/product-offer/src/Checkout.jsx" tag="product-offer-pre-purchase.ext-index"/>

    <CodeRef
    extension="javascript"
    href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--js/blob/main/extensions/pre-purchase-offer/src/Checkout.js" tag="product-offer-pre-purchase.ext-index"/>

      ### Export the target from your script file

      In your <If extension="react">`Checkout.jsx`</If><If extension="javascript">`Checkout.js`</If> file, set the entrypoints for the checkout extension, and then export them so they can be referenced in your configuration.

      For each target that you want to use, create <If extension="react"> a `reactExtension`</If> <If extension="javascript">an `extension`</If> function that references your target, and export it using the default export.

      ---

      This example code uses the default `purchase.checkout.block.render` target. This target lets merchants choose where they want the extension to appear using the [checkout editor](https://help.shopify.com/manual/checkout-settings/checkout-extensibility/checkout-editor), and will render regardless of which checkout features are available.

      You can [update the checkout URL](/docs/apps/build/checkout/test-checkout-ui-extensions#dynamic-extension-points) to test the extension in different locations in the checkout.

      If you want the extension to render in only certain places, then use a [static target](/docs/api/checkout-ui-extensions/latest/extension-targets-overview#static-extension-targets).

      <If extension="react">
      <Resources>
        [reactExtension](/docs/api/checkout-ui-extensions#extension-targets)
        [purchase.checkout.block.render](/docs/api/checkout-ui-extensions/latest/targets/block/purchase-checkout-block-render)
      </Resources>
      </If>
      <If extension="javascript">
      <Resources>
        [extension](/docs/api/checkout-ui-extensions#extension-targets)
        [purchase.checkout.block.render](/docs/api/checkout-ui-extensions/latest/targets/block/purchase-checkout-block-render)
      </Resources>
      </If>

    </Substep>

    <Substep>

    <CodeRef
    extension="react"
    href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--react/blob/main/extensions/product-offer/shopify.extension.toml" tag="product-offer-pre-purchase.ext-config"/>

    <CodeRef
    extension="javascript"
    href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--js/blob/main/extensions/pre-purchase-offer/shopify.extension.toml" tag="product-offer-pre-purchase.ext-config"/>

    <CheckoutUiReference />

    </Substep>

  </Step>

  <Step>
    ## Retrieve product data

    Now that you've set up the extension target, you'll set up products so that you can display them to customers for the product offer.

    <Substep>

      <CodeRef
      extension="react"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--react/blob/main/extensions/product-offer/shopify.extension.toml" tag="product-offer-pre-purchase.api-access"/>

      <CodeRef
      extension="javascript"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--js/blob/main/extensions/pre-purchase-offer/shopify.extension.toml" tag="product-offer-pre-purchase.api-access"/>

      ### Request API access

      Configure your extension to make calls to the Storefront API. In your checkout UI extension's configuration file, create an `[extensions.capabilities]` section with `api_access` set to `true`.

      ---

      [Learn more about requesting Storefront API access for your extension](/docs/api/checkout-ui-extensions/latest/configuration#api-access).

      To retrieve data from an external source using a checkout UI extension, you need to request the [network access](/docs/api/checkout-ui-extensions/latest/configuration#network-access) capability instead of the API access capability.
    </Substep>
    <Substep>

      <CodeRef
      extension="react"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--react/blob/main/extensions/product-offer/src/Checkout.jsx" tag="product-offer-pre-purchase.retrieve-products"/>

      <CodeRef
      extension="javascript"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--js/blob/main/extensions/pre-purchase-offer/src/Checkout.js" tag="product-offer-pre-purchase.retrieve-products"/>

      ### Retrieve products

      Set up a function that handles retrieving your products, using the `query` helper function of the `StandardApi` Checkout API object.

      Query the Storefront API `Products` resource. This query fetches the first five products in the store. Store the products in an array so you can reference the data later.

      ---

      The `StandardApi` Checkout API object is automatically made available to all targets.

      In a production-ready app, you might query your own database to retrieve a list of products to offer.

      For more examples of [querying the Storefront API](https://shopify.dev/docs/api/checkout-ui-extensions/latest/configuration#api-access), refer to the [Storefront API learning kit](https://github.com/Shopify/storefront-api-learning-kit).

      <Resources>
        [query](/docs/api/checkout-ui-extensions/latest/apis/standardapi#properties-propertydetail-query)
        [StandardApi](/docs/api/checkout-ui-extensions/latest/apis/standardapi)
        [Products](/docs/api/storefront/latest/queries/products)
      </Resources>
    </Substep>

    <Substep>

      <CodeRef
      extension="react"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--react/blob/main/extensions/product-offer/src/Checkout.jsx" tag="product-offer-pre-purchase.retrieve-cart-data"/>

      <CodeRef
      extension="javascript"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--js/blob/main/extensions/pre-purchase-offer/src/Checkout.js" tag="product-offer-pre-purchase.retrieve-cart-data"/>

      ### Retrieve cart data

      <If extension="react">
      Use the `useCartLines` hook to retrieve the current line items of the cart.
      </If>
      <If extension="javascript">
      Subscribe to cart data using the `lines` property of the `StandardApi` Checkout API object.
      </If>

      ---

      <If extension="react">
      `useCartLines` is a React hook that lets you subscribe to cart data. To learn more about the hooks available for Checkout UI extensions, refer to the [Checkout UI extension reference](/docs/api/checkout-ui-extensions/latest/react-hooks).

      <Resources>
      [useCartLines](/docs/api/checkout-ui-extensions/latest/react-hooks/cart/usecartlines)
      </Resources>
      </If>

      <If extension="javascript">
      Some API property values might change after the extension is rendered. `StatefulRemoteSubscribable` properties allow you to subscribe to changes and perform a function or re-render your extension.

      <Resources>
        [lines](/docs/api/checkout-ui-extensions/latest/apis/standardapi#properties-propertydetail-lines)
        [StandardApi](/docs/api/checkout-ui-extensions/latest/apis/standardapi)
      </Resources>
      </If>

    </Substep>

    <Substep>

      <CodeRef
      extension="react"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--react/blob/main/extensions/product-offer/src/Checkout.jsx" tag="product-offer-pre-purchase.filter-products"/>

      <CodeRef
      extension="javascript"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--js/blob/main/extensions/pre-purchase-offer/src/Checkout.js" tag="product-offer-pre-purchase.filter-products"/>

      ### Filter products

      Create a function to compare the cart contents to the products that you retrieved from the Storefront API to ensure that you don't offer a product that's already in the cart.

    </Substep>

  </Step>


  <Step>
    ## Build the pre-purchase offer UI

    Build a basic user interface, loading state, and error handling using components from the checkout UI extensions component library.

    <Substep>

      <CodeRef
      extension="react"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--react/blob/main/extensions/product-offer/src/Checkout.jsx" tag="product-offer-pre-purchase.add-to-cart"/>

      <CodeRef
      extension="javascript"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--js/blob/main/extensions/pre-purchase-offer/src/Checkout.js" tag="product-offer-pre-purchase.add-to-cart"/>

      ### Add add-to-cart functionality

      <If extension="react">
      Use the `useApplyCartLinesChange` hook to mutate the `lines` property of the checkout.

      ---

      `useApplyCartLinesChange` is a React hook that lets you write cart information. To learn more about the hooks available for Checkout UI extensions, refer to the [Checkout UI extension reference](/docs/api/checkout-ui-extensions/latest/react-hooks).

      <Resources>
        [useApplyCartLinesChange](/docs/api/checkout-ui-extensions/latest/react-hooks/cart/useapplycartlineschange)
      </Resources>
      </If>

      <If extension="javascript">
      Use the `applyCartLinesChange` helper function of the `StandardApi` Checkout API object to mutate the `lines` property of the checkout.

      ---

      <Resources>
        [applyCartLinesChange](/docs/api/checkout-ui-extensions/latest/apis/standardapi#properties-propertydetail-applycartlineschange)
        [StandardApi](/docs/api/checkout-ui-extensions/latest/apis/standardapi)
      </Resources>
      </If>

    </Substep>

    <Substep>

      <CodeRef
      extension="react"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--react/blob/main/extensions/product-offer/src/Checkout.jsx" tag="product-offer-pre-purchase.offer-ui"/>

      <CodeRef
      extension="javascript"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--js/blob/main/extensions/pre-purchase-offer/src/Checkout.js" tag="product-offer-pre-purchase.offer-ui"/>

      ### Build the offer UI

      Using Checkout UI components, build a basic UI for the product offer.

      ---

      Checkout UI extensions are limited to specific UI components exposed by the platform [for security reasons](/docs/api/checkout-ui-extensions#security). Checkout UI components allow you to create a UI that feels seamless within the checkout experience, and that inherits a merchant's brand settings.

      <Resources>
        [BlockStack](/docs/api/checkout-ui-extensions/latest/components/structure/blockstack)
        [Divider](/docs/api/checkout-ui-extensions/latest/components/structure/divider)
        [Heading](/docs/api/checkout-ui-extensions/latest/components/titles-and-text/heading)
        [InlineLayout](/docs/api/checkout-ui-extensions/latest/components/structure/inlinelayout)
        [Image](/docs/api/checkout-ui-extensions/latest/components/media/image)
        [Text](/docs/api/checkout-ui-extensions/latest/components/titles-and-text/text)
      </ Resources>

    </Substep>

    <Substep>

      <CodeRef
      extension="react"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--react/blob/main/extensions/product-offer/src/Checkout.jsx" tag="product-offer-pre-purchase.loading-state"/>

      <CodeRef
      extension="javascript"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--js/blob/main/extensions/pre-purchase-offer/src/Checkout.js" tag="product-offer-pre-purchase.loading-state"/>

      ### Build a loading state

      Use checkout components, including `SkeletonText` and `SkeletonImage`, to build a loading state to display while the product variants are being fetched.

      ---

      A loading state lets the customer know that content is being rendered, improving the perceived performance of the checkout.

      <Resources>
        [BlockStack](/docs/api/checkout-ui-extensions/latest/components/structure/blockstack)
        [Button](/docs/api/checkout-ui-extensions/latest/components/actions/button)
        [Divider](/docs/api/checkout-ui-extensions/latest/components/structure/divider)
        [Heading](/docs/api/checkout-ui-extensions/latest/components/titles-and-text/heading)
        [InlineLayout](/docs/api/checkout-ui-extensions/latest/components/structure/inlinelayout)
        [SkeletonImage](/docs/api/checkout-ui-extensions/latest/components/feedback/skeletonimage)
        [SkeletonText](/docs/api/checkout-ui-extensions/latest/components/feedback/skeletontext)
      </Resources>
    </Substep>

    <Substep>

    <CodeRef
      extension="react"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--react/blob/main/extensions/product-offer/src/Checkout.jsx" tag="product-offer-pre-purchase.error-ui"/>

    <CodeRef
      extension="javascript"
      href="https://github.com/Shopify/example-checkout--product-offer-pre-purchase--js/blob/main/extensions/pre-purchase-offer/src/Checkout.js" tag="product-offer-pre-purchase.error-ui"/>

      ### Build an error handling UI

      If there's an error adding a product on offer, then use the `Banner` component to communicate any errors to the user.

      For example, you might need to display an error to the customer if the offered product is out of stock.

      ---

      <Resources>
        [Banner](/docs/api/checkout-ui-extensions/latest/components/feedback/banner)
      </Resources>

    </Substep>

  </Step>

  <Step>

    <CheckoutUiPreview extension="pre-purchase offer " />

  </Step>

  <Step>
    ## Test the extension

    Preview your new code and ensure that the extension works as designed.

    <Substep>
      ### Test the extension functionality

      1. With your server running, open the storefront of your development store.
      2. Add a product to the cart and then check out.

          Your placeholder extension is replaced with your new extension code that offers a product from the store, under the heading **You might also like**.

      3. Click **Add** to add the product to the cart.

          The product is added to the cart, and the offer is replaced with a new offer. If the product is out of stock, then an error is returned and the offer is replaced with a new offer.

      4. Continue adding products to the cart using the **Add** button.

          After you add multiple offered products to the cart, the extension is hidden.

      5. Complete the checkout.

      In the [Orders](https://shopify.com/admin/orders) area of the Shopify admin, a new order appears.  The order contains the products that you added to the cart, including the offered products.

      ---

      ![The product offer section in the checkout.](/assets/apps/checkout/product-offer.png)

      <Troubleshooting>

        #### Property token error

        If you receive the error `ShopifyCLI:AdminAPI requires the property token to be set`, then you need to use the [`--checkout-carturl](/docs/api/shopify-cli/app/app-dev#flags) flag to direct Shopify CLI to open a checkout session for you.

        <Codeblock terminal>
        ```bash
        shopify app dev --checkout-cart-url cart/{product_variant_id}:{quantity}
        ```
        </Codeblock>

      </Troubleshooting>
    </Substep>
  </Step>

  <Deploy />

</StepSection>

<NextSteps>
  ## Tutorial complete!

  Nice work - what you just built could be used by Shopify merchants around the world! Keep the momentum going with these related tutorials and resources.

  ### Next steps

  <CardGrid>
    <LinkCard href="/docs/apps/checkout/product-offers/pre-purchase/ux-guidelines">
      #### Review product offer UX guidelines
      Build a good pre-purchase product offer experience by following our UX guidelines.
    </LinkCard>

    <LinkCard href="/docs/apps/checkout/localizing-ui-extensions">
      #### Localize your extension
      Learn how to localize the text and number formats in your extension.
    </LinkCard>

    <LinkCard href="/docs/api/checkout-ui-extensions/latest/components">
      #### Explore the checkout UI extension component reference
      Learn about all of the components that you can use in your checkout UI extension.
    </LinkCard>

    <LinkCard href="/docs/api/checkout-ui-extensions/latest/apis/extensiontargets">
      #### Explore the checkout UI extension targets API reference
      Learn about the extension targets offered in the checkout.
    </LinkCard>
  </CardGrid>
</NextSteps>