---
gid: 9ad299f7-c3a6-4026-9289-37d818259abe
title: Build a discounts UI with Remix
description: Create a UI that merchants can use to configure your discount function.
---
import RequirementPartial from 'app/views/partials/apps/discounts/requirements_partial.mdx';
import ReviewFunctionExecution from 'app/views/partials/apps/app-extensions/review-function-execution.md';

<Repo extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix" />
<Repo extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix" />

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

<Overview>
  Merchants create and manage discounts in the Shopify admin. Shopify uses the [URLs that you configure](/docs/apps/build/functions/input-output/metafields-for-input-queries#creating-your-merchant-interface) to render the discount creation and editing experience for merchants. You can customize this UI for your function's configuration needs or to meet other requirements of your app.

  ## What you'll learn

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

  - Define what configuration settings will be surfaced to merchants.
  - Read and use the merchant-defined values in your function.
  - Create an App Bridge UI that enables merchants to create a [function owner](/docs/apps/build/functions/input-output/metafields-for-input-queries#how-it-works).
  - Configure the UI paths for your function.

  ![The UI for configuring the discount](/assets/apps/discounts/discount-function-merchant-ui.png)
</Overview>

<Requirements>
  <RequirementPartial />

  <Requirement href="/docs/api/app-bridge-library" label="App Bridge library">
    You're using the latest version of the App Bridge library.
  </Requirement>

  <Requirement href="/docs/apps/build/discounts/build-discount-function" label="Build a discount function">
    Complete or copy the code from the Build a discount function tutorial
  </Requirement>
</Requirements>

<StepSection>
  <Step>
    ## Configure the function

    To enhance the reusability of your function, you can replace hardcoded values with [metafield values](/docs/apps/build/custom-data#about-metafields).

    You'll update your [input query](/docs/apps/build/functions/input-output#input) to request a metafield value on the created product discount, which is the [function owner](/docs/apps/build/functions/input-output/metafields-for-input-queries#how-it-works) for this API.

    The metafield value will be used to configure the discount function. This value can be set by the merchant in the Shopify admin, making the discount function more flexible and customizable.

    <Substep>
      ### Update `run.graphql` to use a metafield namespace

      <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/extensions/product-discount/src/run.graphql" tag="build-the-ui.use-namespace" />
      <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/extensions/product-discount-js/src/run.graphql" tag="build-the-ui.use-namespace" />


      Add [`discountNode.metafield`](/docs/api/functions/reference/order-discounts/graphql/common-objects/discountnode) to your input query for a dynamic function configuration with the `volume` and `percentage` fields. Use a metafield namespace with a [reserved prefix](/docs/apps/build/custom-data/reserved-prefixes#reserved-prefixes) to ensure that it's exclusive to your app and inaccessible to others.
    </Substep>

    <br/>
    <Notice type="info">
      The query differs slightly in Rust and JavaScript due to code generation requirements. This change is automatically reflected as long as you're running `dev`.
    </Notice>

    <Substep>
      <If extension="rust">
        ### Update `run.rs` to use the metafield value
      </If>
      <If extension="javascript">
        ### Update `run.js` to use the metafield value
      </If>

      <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/extensions/product-discount/src/run.rs" tag="function-configuration.create-a-structure" />
      <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/extensions/product-discount-js/src/run.js" tag="function-configuration.parse-metafield" />
      <If extension="rust">
        Create a structure that matches the JSON structure that you'll use for your configuration and parse the JSON metafield value using `serde`.
      </If>
      <If extension="javascript">
        Define a type for your configuration and parse it from the metafield.
      </If>
    </Substep>

    <Substep>
      <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/extensions/product-discount/src/run.rs"  tag="function-configuration.use-metafield" />
      <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/extensions/product-discount-js/src/run.js"  tag="function-configuration.use-metafield" />
      Use the configuration from the metafield on your function owner instead of the hardcoded `percentage` and `volume` values.
    </Substep>
  </Step>

  <Step>

    ## Set up discounts dependencies

    The [@shopify/discount-app-components](https://www.npmjs.com/package/@shopify/discount-app-components) package provides useful components for building a discount function configuration experience. The [@shopify/react-form](https://www.npmjs.com/package/@shopify/react-form) library provides hooks to help you manage React forms. Using these libraries requires setting up some dependencies on top of the Remix app template.

    <Substep>
      ### Install packages

        - [@shopify/discount-app-components](https://www.npmjs.com/package/@shopify/discount-app-components): Provides components that you can use to render a discounts UI.
        - [@shopify/react-form](https://www.npmjs.com/package/@shopify/react-form): Provides hooks to help you manage React forms.

      <Codeblock terminal>

        ```bash title="npm"
        npm install @shopify/discount-app-components @shopify/react-form
        ```
        ```bash title="Yarn"
        yarn add @shopify/discount-app-components @shopify/react-form
        ```
        ```bash title="pnpm"
        pnpm add @shopify/discount-app-components @shopify/react-form
        ```

      </Codeblock>
    </Substep>

  </Step>
  <Step>
    ## Add the `AppProvider` to your app
    The `@shopify/discount-app-components` library exports an `AppProvider` component that must be included in your app to ensure all other components in the library function correctly within the Shopify admin.
    <Substep>
      <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.jsx" tag="build-the-ui.render-the-provider" />
      <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.jsx" tag="build-the-ui.render-the-provider" />
      ### Render the `AppProvider as DiscountProvider` in app.jsx

      In `app.jsx`, import and render `AppProvider` from `@shopify/discount-app-components`.
    </Substep>
  </Step>


  <Step>
    ## Create the frontend UI for your function

    Create a UI that enables merchants to configure the volume discount. This UI will allow merchants to set the minimum quantity and the discount percentage for the volume discount.

    <Substep>
      ### Create a new route for the volume discount

      In `app/routes`, create a new file named `app.volume-discount.$functionId.new.jsx`.
    </Substep>
    <Substep>
      ### Add a Remix `action` function to handle form submission

      <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.volume-discount.$functionId.new.jsx" tag="build-the-ui.add-action" />
      <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.volume-discount.$functionId.new.jsx" tag="build-the-ui.add-action" />
      The [`action`](https://remix.run/docs/en/main/route/action) function handles submitting the form data to Shopify. This is a server-side action that's invoked when the form is submitted. It makes a request to the GraphQL Admin API's [`discountCodeAppCreate`](/docs/api/admin-graphql/latest/mutations/discountCodeAppCreate) mutation, to create a discount.
    </Substep>

    <Substep>
      ### Create a form to collect the volume discount fields
      <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.volume-discount.$functionId.new.jsx" tag="build-the-ui.create-form" />
      <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.volume-discount.$functionId.new.jsx" tag="build-the-ui.create-form" />
      Create a form to collect the volume discount fields. The form uses the `useForm` hook from `@shopify/react-form` to manage form state and validation.
    </Substep>

    <Substep>
      ### Add configuration fields to the form

      <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.volume-discount.$functionId.new.jsx" tag="build-the-ui.add-configuration" />
      <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.volume-discount.$functionId.new.jsx" tag="build-the-ui.add-configuration" />
      In the `VolumeNew` component, add a configuration field with `quantity` and `percentage` to collect the volume discount configuration that will be stored in your metafield. These configuration properties will define initial states for your form data.

      <Notice type="tip">
        You can store your configuration in one or more metafields on a discount. Learn more about [using metafields with input queries](/docs/apps/build/functions/input-output/metafields-for-input-queries).
      </Notice>
    </Substep>


    <Substep>
      ### Add Polaris components for the configuration fields
      <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.volume-discount.$functionId.new.jsx" tag="build-the-ui.add-a-card" />
      <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.volume-discount.$functionId.new.jsx" tag="build-the-ui.add-a-card" />
      Use the `Card`, `BlockStack`, `TextField`, and `Text` components from  the `@shopify/polaris` library to render the configuration fields for the volume discount.
    </Substep>

    <Substep>
      ### Add Discount App Components for the remaining discount fields
      <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.volume-discount.$functionId.new.jsx" tag="build-the-ui.other-components" />
      <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.volume-discount.$functionId.new.jsx" tag="build-the-ui.other-components" />
      Use the `MethodCard`, `UsageLimitsCard`, `CombinationCard` and `ActiveDatesCard` from the `@shopify/discount-app-components` library to render the remaining fields for the  [`discountCodeAppCreate`](/docs/api/admin-graphql/latest/mutations/discountCodeAppCreate) and [`discountAutomaticAppCreate`](/docs/api/admin-graphql/latest/mutations/discountAutomaticAppCreate) mutations.
    </Substep>
  </Step>

  <Step>
    ## Configure the create UI path for your function

    Settings in the `shopify.extension.toml` file define the URLs that Shopify uses to enable merchants to create and edit discounts based on your function. Shopify automatically fills in any [dynamic tokens](/docs/apps/build/functions/input-output/metafields-for-input-queries#dynamic-id-values) in these URLs.

    <Substep>
      ### Update the `ui.paths`

      <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/extensions/product-discount/shopify.extension.toml" tag="build-the-ui.add-ui-path" />
      <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/extensions/product-discount-js/shopify.extension.toml" tag="build-the-ui.add-ui-path" />
      In `shopify.extension.toml`, populate the settings directly under `[ui.paths]`. This change is automatically reflected as long as you're running `dev`.
    </Substep>

    ---
    <Notice type="info">
      This tutorial doesn't cover the creation of the `details` UI defined here. To enable app merchants to edit your discount, you must also create this route at `app.volume-discount.$functionId.$id.jsx`.
    </Notice>
  </Step>

  <Step>
    ## Create a discount function
    1. If your app isn't already running, then start it using Shopify CLI:
        <Codeblock terminal>
        ```bash
          shopify app dev
        ```
        </Codeblock>

    1. From your Shopify admin, go to **Discounts**.
    1. If you have existing discounts from previous tutorials, then click the checkbox next to each of them, and then click **Deactivate discounts**.
    1. Click the **Create discount** button.
    1. Click the **product-discount** discount type that you created under your app name. You should see the create page for your Volume discount.
    1. Fill in values for the discount:
        - For **Method**, use **Automatic**.
        - For **Title**, use **Volume**.
        - For **Minimum quantity**, use **4**.
        - For **Discount percentage**, use **20**.
    1. Click **Save**.
        You should see a new discount in the discounts list.
    </Step>
    <Step>
    ## Test your discount function
    <Substep>
    ### Checkout using your discount
    1. Open your development store and build a cart with a single item in it.
      No discounts should be applied to the cart.
    1. Increase the item quantity to 4.
      The 20% **Volume discount** should now be applied to the cart.
    </Substep>
    <Substep>
      <ReviewFunctionExecution />
    </Substep>

</Step>

</StepSection>


<NextSteps>
  ## Next Steps
  <CardGrid>
    <LinkCard href="/docs/apps/build/functions">
      #### Learn more about Shopify Functions

      Learn more about how Shopify Functions work and the benefits of using Shopify Functions.
    </LinkCard>

    <LinkCard href="/docs/api/functions">
      #### Consult the Shopify Functions API references

      Consult the API references for Shopify Functions
    </LinkCard>

    <LinkCard href="/docs/apps/build/discounts/ux-for-discounts">
      #### Review the UX guidelines

      Review the UX guidelines to learn how to implement discounts in user interfaces.
    </LinkCard>
  </CardGrid>
</NextSteps>