--- 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>