--- title: Build for order action menus description: Learn how to build order action menu extensions that allows customers to report issues with their orders. source_url: html: https://shopify.dev/docs/apps/build/customer-accounts/order-action-extensions/build-for-order-action-menus md: https://shopify.dev/docs/apps/build/customer-accounts/order-action-extensions/build-for-order-action-menus.md --- # Build for order action menus In this tutorial, you'll create an order action menu extension that allows customers to report problems with their orders. ## What you'll learn In this tutorial, you'll learn how to do the following tasks: * Create an order action menu extension that opens a modal * Make an API call to the [Customer Account API](https://shopify.dev/docs/api/customer), to retrieve the order's fulfillments * Conditionally render the order action menu extension based on the order's fulfillment status * Conditionally alter the extension's behavior if the customer belongs to a company, for B2B * Run the extension locally and test it on a development store ## Requirements [Create a Partner account](https://www.shopify.com/partners) [Create a development store](https://shopify.dev/docs/apps/tools/development-stores) The development store should be pre-populated with [test data](https://shopify.dev/docs/api/development-stores/generated-test-data), including an order associated with the email address you'll use to log in to the customer account experience. [Shopify CLI](https://shopify.dev/docs/apps/tools/cli/installation) You'll need to use the [latest version of Shopify CLI](https://shopify.dev/docs/api/shopify-cli#upgrade). [Scaffold an app](https://shopify.dev/docs/apps/build/scaffold-app) Scaffold an app that uses Shopify CLI. ## Project [View on GitHub](https://github.com/Shopify/customer-account-tutorials/tree/main/preact/example-customer-account--order-action-menu--preact) ### Create a customer account UI extension To create a customer account UI extension, you can use Shopify CLI, which generates starter code for building your extension and automates common development tasks. 1. Navigate to your app directory: ## Terminal ```terminal cd ``` 2. Run the following command to create a new customer account UI extension: ## Terminal ```terminal shopify app generate extension --template customer_account_ui --name customer-account-ui-extension ``` You should now have a new extension directory in your app's directory. The extension directory includes the extension script at `src/{FileName}.jsx`. The following is an example directory structure: ## Customer account UI extension file structure ```text └── my-app └── extensions └── my-customer-account-ui-extension ├── src │ └── CustomerAccount.jsx // The index page of the customer account UI extension ├── locales │ ├── en.default.json // The default locale for the customer account UI extension │ └── fr.json // The locale file for non-regional French translations ├── shopify.extension.toml // The config file for the customer account UI extension └── package.json ``` 1) Start your development server to build and preview your app: ## Terminal ```terminal shopify app dev ``` To learn about the processes that are executed when you run `dev`, refer to the [Shopify CLI command reference](https://shopify.dev/docs/api/shopify-cli/app/app-dev). 2) Press `p` to open the developer console. In the developer console page, click on the preview link for your extension. ### Configure access scopes for your app In your app’s `shopify.app.toml` file, include the required [access scopes](https://shopify.dev/docs/apps/build/cli-for-apps/app-configuration#access_scopes). The example code needs the `customer_read_orders` access scope to read the `ORDER` resource with the GraphQL Admin API. To read on the `CUSTOMER` and `ORDER` resources using the Customer Account API, the `customer_read_customers` and `customer_read_orders` scopes will be required. ## /preact/example-customer-account--order-action-menu--preact/shopify.app.toml ```toml name = "ca-order-action-menu" application_url = "https://shopify.dev/apps/default-app-home" embedded = true [build] automatically_update_urls_on_dev = true include_config_on_deploy = true [webhooks] api_version = "2025-10" [access_scopes] scopes = "customer_read_customers,customer_read_orders" [auth] redirect_urls = [ "https://shopify.dev/apps/default-app-home/api/auth" ] ``` ### Set up the targets for your extension Set up the targets for your customer account UI extension. [Extension targets](https://shopify.dev/docs/api/customer-account-ui-extensions/targets) control where your extension renders in the customer account flow. You'll use static extension targets to render a button on the **Order index** and **Order status** pages and to render a modal when the button is clicked. #### Reference the extension targets in your configuration file This example code uses the following targets: [customer-account.order.action.menu-item.render](https://shopify.dev/docs/api/customer-account-ui-extensions/targets/order-action-menu/customer-account-order-action-menu-item-render)[customer-account.order.action.render](https://shopify.dev/docs/api/customer-account-ui-extensions/targets/order-action-menu/customer-account-order-action-render) In your extension's [`shopify.extension.toml`](https://shopify.dev/docs/apps/build/app-extensions/configure-app-extensions) configuration file, for each of the targets, create an `[[extensions.targeting]]` section with the following information: * **`target`**: An identifier that specifies where you're injecting code into Shopify. * **`module`**: The path to the file that contains the extension code. *** ## /preact/example-customer-account--order-action-menu--preact/extensions/action-menu-extension-preact/shopify.extension.toml ```toml api_version = "2025-10" [[extensions]] uid = "80e9f109-e0c9-ab13-2c5a-e065beb9d2eb669d1292" type = "ui_extension" name = "action-menu-extension-preact" handle = "action-menu-extension-preact" [[extensions.targeting]] module = "./src/MenuActionExtension.jsx" target = "customer-account.order.action.menu-item.render" [[extensions.targeting]] module = "./src/MenuActionModalExtension.jsx" target = "customer-account.order.action.render" ``` #### Create files for your targets Create files in your extension's `src` directory for each of your targets. In this example, you'll create a file for the order action menu extension and a file for the order action modal extension. The filenames must match the `module` paths you specified. ### Build the order action menu item #### Fetch the order's fulfillments Merchants might not want to allow customers to report a problem before at least some parts of an order are fulfilled. To determine the fulfillment status, retrieve the order's fulfillments from the [Customer Account API](https://shopify.dev/docs/api/customer). Make sure to fetch data before rendering the button the first time. ## /preact/example-customer-account--order-action-menu--preact/extensions/action-menu-extension-preact/src/MenuActionExtension.jsx ```jsx import {render} from 'preact'; export default async () => { let hasFulfillments = false; try { const orderQuery = { query: `query Order($orderId: ID!) { order(id: $orderId) { fulfillments(first: 1) { nodes { latestShipmentStatus } } } }`, variables: {orderId: shopify.orderId}, }; const result = await fetch( 'shopify://customer-account/api/2025-10/graphql.json', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(orderQuery), } ); const {data} = await result.json(); hasFulfillments = data.order.fulfillments.nodes.length !== 0; } catch (error) { console.log(error); hasFulfillments = false; } render(, document.body); }; function MenuActionExtension({showAction}) { if (!showAction) { return null; } return Report a problem; } ``` #### Conditionally render the order action menu item, based on the order's fulfillment status Add a condition to render `null` if the order doesn't have any fulfillments. ## /preact/example-customer-account--order-action-menu--preact/extensions/action-menu-extension-preact/src/MenuActionExtension.jsx ```jsx import {render} from 'preact'; export default async () => { let hasFulfillments = false; try { const orderQuery = { query: `query Order($orderId: ID!) { order(id: $orderId) { fulfillments(first: 1) { nodes { latestShipmentStatus } } } }`, variables: {orderId: shopify.orderId}, }; const result = await fetch( 'shopify://customer-account/api/2025-10/graphql.json', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(orderQuery), } ); const {data} = await result.json(); hasFulfillments = data.order.fulfillments.nodes.length !== 0; } catch (error) { console.log(error); hasFulfillments = false; } render(, document.body); }; function MenuActionExtension({showAction}) { if (!showAction) { return null; } return Report a problem; } ``` #### Render the button Now you'll render the order action button. The button isn't being passed a `to` or `onPress` prop. This is how we know to connect this button to the order action modal extension. *** ![Order action menu](https://cdn.shopify.com/shopifycloud/shopify-dev/production/assets/assets/apps/customer-accounts/report-problem-button-CVpGbt9A.png) ## /preact/example-customer-account--order-action-menu--preact/extensions/action-menu-extension-preact/src/MenuActionExtension.jsx ```jsx import {render} from 'preact'; export default async () => { let hasFulfillments = false; try { const orderQuery = { query: `query Order($orderId: ID!) { order(id: $orderId) { fulfillments(first: 1) { nodes { latestShipmentStatus } } } }`, variables: {orderId: shopify.orderId}, }; const result = await fetch( 'shopify://customer-account/api/2025-10/graphql.json', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(orderQuery), } ); const {data} = await result.json(); hasFulfillments = data.order.fulfillments.nodes.length !== 0; } catch (error) { console.log(error); hasFulfillments = false; } render(, document.body); }; function MenuActionExtension({showAction}) { if (!showAction) { return null; } return Report a problem; } ``` ### Build the order action modal #### Check if this is a B2B customer Add an extra option in the `s-select` field options if the customer belongs to a company. ## /preact/example-customer-account--order-action-menu--preact/extensions/action-menu-extension-preact/src/MenuActionModalExtension.jsx ```jsx import '@shopify/ui-extensions/preact'; import {render} from 'preact'; import {useState} from 'preact/hooks'; export default async () => { render(, document.body); }; const dtcOptions = [ {value: '1', label: 'Package item is damaged'}, {value: '2', label: 'Missing items'}, {value: '3', label: 'Wrong item was sent'}, {value: '4', label: 'Item arrived too late'}, {value: '5', label: 'Never received item'}, ]; const b2bOptions = dtcOptions.concat([ {value: '6', label: 'Package sent to the wrong company location'}, ]); function MenuActionModalExtension() { const [currentProblem, setCurrentProblem] = useState('1'); const [isLoading, setIsLoading] = useState(false); const isB2BCustomer = shopify.authenticatedAccount.purchasingCompany.value != null; function onSubmit() { // Simulating a request to your server to store the reported problem setIsLoading(true); console.log('Problem reported: ', currentProblem); setTimeout(() => { setIsLoading(false); shopify.close(); }, 750); } const options = isB2BCustomer ? b2bOptions : dtcOptions; return ( setCurrentProblem(e.target.value)} > {options.map((option) => ( {option.label} ))} onSubmit()} > Report shopify.close()}> Cancel ); } ``` #### Build the modal's UI Use `s-customer-account-action` to extend the target. Here, you'll use `s-select` to display a list of reasons for reporting the order. *** ![Order action modal](https://cdn.shopify.com/shopifycloud/shopify-dev/production/assets/assets/apps/customer-accounts/report-problem-modal-WUFzv4nj.png) ## /preact/example-customer-account--order-action-menu--preact/extensions/action-menu-extension-preact/src/MenuActionModalExtension.jsx ```jsx import '@shopify/ui-extensions/preact'; import {render} from 'preact'; import {useState} from 'preact/hooks'; export default async () => { render(, document.body); }; const dtcOptions = [ {value: '1', label: 'Package item is damaged'}, {value: '2', label: 'Missing items'}, {value: '3', label: 'Wrong item was sent'}, {value: '4', label: 'Item arrived too late'}, {value: '5', label: 'Never received item'}, ]; const b2bOptions = dtcOptions.concat([ {value: '6', label: 'Package sent to the wrong company location'}, ]); function MenuActionModalExtension() { const [currentProblem, setCurrentProblem] = useState('1'); const [isLoading, setIsLoading] = useState(false); const isB2BCustomer = shopify.authenticatedAccount.purchasingCompany.value != null; function onSubmit() { // Simulating a request to your server to store the reported problem setIsLoading(true); console.log('Problem reported: ', currentProblem); setTimeout(() => { setIsLoading(false); shopify.close(); }, 750); } const options = isB2BCustomer ? b2bOptions : dtcOptions; return ( setCurrentProblem(e.target.value)} > {options.map((option) => ( {option.label} ))} onSubmit()} > Report shopify.close()}> Cancel ); } ``` #### Make an API call to store the reported problem The example code adds a timeout to fake an API call. In a production-ready application, make an API call to your server to store the reported problem. ## /preact/example-customer-account--order-action-menu--preact/extensions/action-menu-extension-preact/src/MenuActionModalExtension.jsx ```jsx import '@shopify/ui-extensions/preact'; import {render} from 'preact'; import {useState} from 'preact/hooks'; export default async () => { render(, document.body); }; const dtcOptions = [ {value: '1', label: 'Package item is damaged'}, {value: '2', label: 'Missing items'}, {value: '3', label: 'Wrong item was sent'}, {value: '4', label: 'Item arrived too late'}, {value: '5', label: 'Never received item'}, ]; const b2bOptions = dtcOptions.concat([ {value: '6', label: 'Package sent to the wrong company location'}, ]); function MenuActionModalExtension() { const [currentProblem, setCurrentProblem] = useState('1'); const [isLoading, setIsLoading] = useState(false); const isB2BCustomer = shopify.authenticatedAccount.purchasingCompany.value != null; function onSubmit() { // Simulating a request to your server to store the reported problem setIsLoading(true); console.log('Problem reported: ', currentProblem); setTimeout(() => { setIsLoading(false); shopify.close(); }, 750); } const options = isB2BCustomer ? b2bOptions : dtcOptions; return ( setCurrentProblem(e.target.value)} > {options.map((option) => ( {option.label} ))} onSubmit()} > Report shopify.close()}> Cancel ); } ``` ### Preview the extension Preview your extension to make sure that it works as expected. #### Start your server Run the Shopify CLI `dev` command to build your app and preview it on your development store. 1. In a terminal, navigate to your app directory. 2. Either start or restart your server to build and preview your app: ## Terminal ```bash shopify app dev ``` 3. If prompted, select a development store. 4. Press `p` to open the developer console. 5. To install the app on your development store, click the **Install your app** link. 6. In the developer console page, click the preview link for one of your extension targets. The customer accounts experience opens. Test your conditional logic by submitting and fulfilling some orders. For example, the order action menu item should only display if the order has at least one fulfillment. *** ## /preact/example-customer-account--order-action-menu--preact/shopify.app.toml ```toml name = "ca-order-action-menu" application_url = "https://shopify.dev/apps/default-app-home" embedded = true [build] automatically_update_urls_on_dev = true include_config_on_deploy = true [webhooks] api_version = "2025-10" [access_scopes] scopes = "customer_read_customers,customer_read_orders" [auth] redirect_urls = [ "https://shopify.dev/apps/default-app-home/api/auth" ] ``` ## 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 [![](https://shopify.dev/images/icons/32/blocks.png)![](https://shopify.dev/images/icons/32/blocks-dark.png)](https://shopify.dev/docs/apps/customer-accounts/best-practices/deciding-extension-placement) [Extension placement](https://shopify.dev/docs/apps/customer-accounts/best-practices/deciding-extension-placement) [Explore extension placement options and make informed decisions on where to position them.](https://shopify.dev/docs/apps/customer-accounts/best-practices/deciding-extension-placement) [![](https://shopify.dev/images/icons/32/globe.png)![](https://shopify.dev/images/icons/32/globe-dark.png)](https://shopify.dev/docs/apps/customer-accounts/best-practices/localizing-ui-extensions) [Localize your extension](https://shopify.dev/docs/apps/customer-accounts/best-practices/localizing-ui-extensions) [Learn about localizing your customer account UI extensions for international merchants and customers.](https://shopify.dev/docs/apps/customer-accounts/best-practices/localizing-ui-extensions) [![](https://shopify.dev/images/icons/32/blocks.png)![](https://shopify.dev/images/icons/32/blocks-dark.png)](https://shopify.dev/docs/api/customer-account-ui-extensions/targets) [Extension targets](https://shopify.dev/docs/api/customer-account-ui-extensions/targets) [Learn about the extension targets offered in customer account.](https://shopify.dev/docs/api/customer-account-ui-extensions/targets) [![](https://shopify.dev/images/icons/32/heart.png)![](https://shopify.dev/images/icons/32/heart-dark.png)](https://shopify.dev/docs/apps/customer-accounts/best-practices/ux-guidelines) [UX guidelines](https://shopify.dev/docs/apps/customer-accounts/best-practices/ux-guidelines) [Follow our UX guidelines for customer accounts to ensure a consistent and satisfying user experience.](https://shopify.dev/docs/apps/customer-accounts/best-practices/ux-guidelines) [![](https://shopify.dev/images/icons/32/blocks.png)![](https://shopify.dev/images/icons/32/blocks-dark.png)](https://shopify.dev/docs/api/customer-account-ui-extensions/polaris-web-components) [Polaris web components](https://shopify.dev/docs/api/customer-account-ui-extensions/polaris-web-components) [Learn about the components you can use to build customer account UI extensions.](https://shopify.dev/docs/api/customer-account-ui-extensions/polaris-web-components)