Skip to main content

Build a discounts UI with Admin UI extensions

With Shopify Functions, you can create new types of discounts that apply to cart lines, order subtotals, shipping rates, or any combination of these.

Additionally, you can use Admin UI extensions to create a UI, displayed on the discount details page of Shopify admin, for merchants to configure your app's Discount Function. To do this, this custom UI will use metafields to communicate with your Discount Function.

This tutorial describes how to create an Admin UI Extension that merchants can use to configure a Discount Function.

The discount details showing an Admin UI Extension block

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

  • Create an Admin UI Extension, written in React and hosted by Shopify
  • Build a Discount Function that applies a percentage off cart lines, order subtotal and shipping rates
  • Associate your Admin UI extension to your Discount Function, so merchants can use the UI to configure the discounts provided by the Discount Function
  • Configure app-scoped metafields to store discount settings

After completing this tutorial, you'll be able to use your Shopify admin to configure a Discount Function that discounts cart lines (filtered by collection), order subtotal, and shipping.

Requirements

Scaffold an app with a Discount Function

For example, follow the Build a Discount Function tutorial, or build your own app and Discount Function using the Discount API.

Project

Anchor to Create an Admin UI ExtensionCreate an Admin UI Extension

First, scaffold the Admin UI extension that will provide the UI that merchants can use to configure your Discount Function.

  1. Navigate to your app's directory:

Terminal

cd directory
  1. Run the following command to create a new Discount Function Settings Admin UI Extension:

Terminal

shopify app generate extension --template discount_details_function_settings --name discount-ui-js --flavor react

Anchor to Configure your Admin UI ExtensionConfigure your Admin UI Extension

Your Admin UI extension will use a metafield to tell your Discount Function which collections of products to discount, and the specific discount percentage to offer for products, order subtotal, and shipping. The Discount Function will use this metafield to dynamically create discount operations.

The Discount Function settings block with the extension name

Specify your extension's metadata in the [[extensions]] section of its associated configuration file. In this case, name and description will appear in the Discounts and discount details page of the Shopify admin.

Info

Shopify provides localization tools, and you can use them to localize the name and description of Discount Functions. To learn more, refer to localizing your Shopify app.

In the [[extensions.targeting]] section of your extension's configuration file, you'll define its target and module.

target specifies where in the Shopify Admin surface the extension should display. For this tutorial, set target to admin.discount-details.function-settings.render.

module specifies a path to the file that contains the main export.

Refer to App Extensions Common properties for more information.

Anchor to Associate your Discount Function with the Admin UI ExtensionAssociate your Discount Function with the Admin UI Extension

Your Admin UI extension can configure your Discount Function. To connect them, open your Function's .toml configuration file and find the [extensions.ui] section. Set the handle property to match the handle in your Admin UI extension's shopify.extension.toml file.

Anchor to Review the Admin UI ExtensionReview the Admin UI Extension

Before continuing on with the tutorial, review the code for your Admin UI extension, so that you understand how it's configured, how it displays data, and how it manages state.

Anchor to Initialize the Admin UI ExtensionInitialize the Admin UI Extension

The discount details page renders the admin.discount-details.function-settings.render target. The target must be provided to the reactExtension function, and must match the extensions.targeting.target defined in the previous step.

Admin UI extensions are rendered using Remote UI, which is a fast and secure remote-rendering framework. Because Shopify renders the UI remotely, components used in the extensions must comply with a contract in the Shopify host. Shopify provides these components through the Admin UI extensions library.

The App component is the main component of the extension. It renders a form with percentage fields and a collection picker. It uses the useExtensionData hook to manage the state of the form.

Anchor to The ,[object Object], and ,[object Object], componentsThe AppliesToCollections and CollectionsSection components

The AppliesToCollections component renders a Select component which allows users to dynamically select Collections from your shop. To do this, it uses the Admin UI Extensions resource picker API.

The CollectionsSection component renders the collections section, displaying the selected collections and a button to add or remove collections.

Anchor to Manage the form's dirty stateManage the form's dirty state

Since the ResourcePicker doesn't directly update the form's state, it doesn't enable the Save action on the discount details page. To fix this, add a hidden TextField, inside a Box component, and use it to store the selected collection IDs as a serialized value. This hidden field integrates with the FunctionSettings component, enabling the form to detect changes and allowing merchants to save their discount settings.

Anchor to Manage state with the ,[object Object], hookManage state with the useExtensionData hook

The useExtensionData hook manages the state of the form. It uses the useApi hook to interact with the GraphQL Admin API. The useEffect hook is used to set the initial state of the form.

Anchor to Metafields in Admin UI ExtensionsMetafields in Admin UI Extensions

Metafields are used to persist shared state between the Discount Function and the Admin UI Extension. Metafields are scoped to an app, and they're unique for each discount created.

Anchor to Review how the code creates metafieldsReview how the code creates metafields

For security reasons, you must create your metafield definition under a reserved namespace. Reserved metafield definitions keep your app's metafields private, and you have to set any additional permissions manually. Because this Admin UI extension runs in Shopify admin, you'll need to request these permissions when installing the app.

Use the $app scope to create a metafield definition under a reserved namespace. The namespace and key must be unique.

App scoped metafield definition

Anchor to Review the code that updates metafieldsReview the code that updates metafields

When a merchant uses your Admin UI extension to update a discount's details, the applyExtensionMetafieldChange function saves the updated form values directly into the discount's metafield in Shopify.

Anchor to Apply cart line and order subtotal discountsApply cart line and order subtotal discounts

Discount Functions return operations which contain discount candidates, that are responsible for applying discounts to cart lines, order subtotals, and shipping rates.

Anchor to Update the Discount Function configuration to use Metafields in input queriesUpdate the Discount Function configuration to use Metafields in input queries

Admin UI extensions can use metafields to share data with Discount Functions.

To allow your Discount Function to receive these metafield values as input variables, update your Function's shopify.extension.toml configuration file. Under [extensions.input.variables], set namepace and key to the same values set in your Admin UI extension code.

Anchor to Use Metafield properties as input variablesUse Metafield properties as input variables

In the previous step, you defined an input variable that uses a discount Metafield. This metafield is queried and updated by the UI Extension.

The input GraphQL query can specify reference fields found in the object stored by a metafield. For example, collectionIds is a key present in the metafield created by the Admin UI extension created above. This is particularly useful when using fields with arguments such as the inAnyCollection field.

Anchor to Use input variables to query ,[object Object], fieldUse input variables to query inAnyCollection field

The inAnyCollection field on the product field allows your Discount Function to dynamically determine whether a given product in the cart belongs to any of many collections.

Anchor to Query the metafield definition to get the discount percentagesQuery the metafield definition to get the discount percentages

The namespace and key must be the same as the namespace and key used in the Admin UI Extension, since that metafield stores the discount percentages configured by the extension.

Anchor to Update the Discount Function to parse the metafieldUpdate the Discount Function to parse the metafield

Parse the metafield which is stored on the discount field to get the function configuration for the cart.lines.discounts.generate.run target.

Anchor to Apply cart line and order subtotal discountsApply cart line and order subtotal discounts

Add the cart line and order subtotal operations, using the percentage values set in the discount metafield configuration.

Anchor to Discount cart lines based on collection membershipDiscount cart lines based on collection membership

Conditionally add targets to the candidates using the line.merchandise.product.inAnyCollection from the input query.

Anchor to Use metafields in Discount Function delivery options targetUse metafields in Discount Function delivery options target

The Delivery Options target is used to apply discounts to shipping rates.

Anchor to Query the metafield definitionQuery the metafield definition

The namespace and key must be the same as the namespace and key used in the Admin UI Extension. This metafield definition is used to obtain the defined percentage for shipping rate discounts.

Anchor to Update the Discount Function to parse the metafieldUpdate the Discount Function to parse the metafield

Parse the metafield on the discount field to get the percentage value to reduce the delivery groups shipping rates.

Anchor to Apply discounts to delivery groupsApply discounts to delivery groups

Add the delivery groups operations using the percentage values from the discount metafield configuration.

Anchor to Request the access scopes for reading collectionsRequest the access scopes for reading collections

To access collections, your app must have the read_products app scope. To set this up, add the read_products scope to your shopify.app.toml file.

Anchor to Deploy your app with new access scopesDeploy your app with new access scopes

  1. Deploy your updated configuration to Shopify:

    Terminal

    shopify app deploy
  2. Restart your app development server:

    Terminal

    shopify app dev
  3. Open or reinstall your app using the URL provided by Shopify CLI. To preview your extensions, press p in the terminal window where you started your app.

  4. Accept the new access scope permissions in your store.

Anchor to Test your Discount FunctionTest your Discount Function

Anchor to Create a discount using your appCreate a discount using your app

  1. In the extension preview console opened in the previous step, click on the preview link for the admin-discount-details.function-settings.render extension target. This opens the discount details page.
  2. Your Admin UI Extension should appear on this page.
  3. Configure your discount, and click Save.

You should see a new discount in the discounts list.

A list of all active discounts for the store.

Anchor to Deactivate other discountsDeactivate other discounts

To ensure that the newly created discount is the only active discount, deactivate or delete all other discounts.

Open your development store and build a cart with a single item in it, use a product that belongs to the collection you configured in the Admin UI Extension.

Anchor to Validate discount applicationValidate discount application

On the cart page, you should see that your Discount Function has applied a discount to a cart line and the order subtotal. When you navigate to the checkout page and provide a shipping address, you'll see the shipping discount.

Anchor to Review the Function executionReview the Function execution

  1. In the terminal where shopify app dev is running, review your Function executions.

    When testing Functions on development stores, the dev output shows Function executions, debug logs you've added, and a link to a local file containing full execution details.

  2. In a new terminal window, use the Shopify CLI command app function replay to replay a Function execution locally. This lets you debug your Function without triggering it again on Shopify.

    Terminal

    shopify app function replay
  3. Select the Function execution from the top of the list. Press q to quit when you are finished debugging.

Was this page helpful?