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>