---
title: Picker API
description: >-
  The Picker API lets merchants search for and select items from your
  app-specific data, such as product reviews, email templates, or subscription
  options. Use this API to build custom selection dialogs with your own data
  structure, badges, and thumbnails.
source_url:
  html: >-
    https://shopify.dev/docs/api/app-home/apis/user-interface-and-interactions/picker-api
  md: >-
    https://shopify.dev/docs/api/app-home/apis/user-interface-and-interactions/picker-api.md
---

# Picker API

The Picker API lets merchants search for and select items from your app-specific data, such as product reviews, email templates, or subscription options. Use this API to build custom selection dialogs with your own data structure, badges, and thumbnails. The picker returns the IDs of selected items.

**Tip:**

If you need to pick Shopify products, variants, or collections, use the [Resource Picker](https://shopify.dev/docs/api/app-home/apis/resource-picker) API instead.

### Use cases

* **Custom resource selection:** Help users find and select app-specific resources like email templates or subscription options.
* **Search interface:** Provide a search-based interface for users to find resources from your app's data.
* **Multi-select:** Allow users to select one or more items from a searchable list.
* **Custom data sources:** Build pickers backed by your own data sources with custom search and filtering.

### Inputs

The `picker` function opens a custom selection dialog with your app-specific data. It accepts configuration options to define the picker's heading, items, headers, and selection behavior. It returns a Promise that resolves to a `Picker` object with a `selected` property for accessing the merchant's selection.

* **heading**

  **string**

  **required**

  The heading displayed at the top of the picker modal. Use a clear, descriptive title that tells merchants what they're selecting.

* **items**

  **PickerItem\[]**

  **required**

  The list of items that merchants can select from. Each item appears as a row in the picker table.

* **headers**

  **Header\[]**

  The column headers for the picker table. Define headers to label and organize the data columns displayed for each item. The header order determines the column layout.

* **multiple**

  **boolean | number**

  **Default: false**

  The selection mode for the picker. Pass `true` to allow unlimited selections, `false` for single-item selection only, or a number to set a maximum selection limit (for example, `3` allows up to three items).

### Header

The configuration for a table column header in the picker. Each header creates a labeled column that displays corresponding data from items.

* content

  The label text displayed at the top of the table column. Use clear, concise labels that describe the data in that column (for example, "Price", "Status", "Last Updated").

  ```ts
  string
  ```

* type

  The data type that controls column formatting and text alignment. Use \`'number'\` for currency, prices, or numeric values (displays right-aligned), or \`'string'\` for text content (displays left-aligned).

  ```ts
  'string' | 'number'
  ```

### PickerItem

An individual item that merchants can select in the picker. Each item appears as a row in the picker table.

* badges

  Status or context badges displayed next to the heading in the first column. Use badges to highlight item state, completion status, or other important attributes (for example, "Draft", "Published", "Incomplete").

  ```ts
  Badge[]
  ```

* data

  Additional data displayed in subsequent columns after the heading. Each value appears in its own column, and the order must match the \`headers\` array. For example, if headers are \`\["Price", "Status"]\`, then data would be \`\[19.99, "Active"]\`.

  ```ts
  DataPoint[]
  ```

* disabled

  Whether the item can be selected. When \`true\`, the item is disabled and can't be selected. Disabled items appear grayed out. Use this for items that are unavailable or don't meet selection criteria.

  ```ts
  boolean
  ```

* heading

  The primary text displayed in the first column. This is typically the item's name or title and is the most prominent text in the row.

  ```ts
  string
  ```

* id

  The unique identifier for this item. This ID is returned in the selection array when the merchant selects this item. Use an ID that helps you identify the item in your system (for example, template IDs, review IDs, or custom option keys).

  ```ts
  string
  ```

* selected

  Whether the item is preselected when the picker opens. When \`true\`, the item appears selected by default. Merchants can still deselect preselected items. Use this to show current selections or suggest default choices.

  ```ts
  boolean
  ```

* thumbnail

  A small preview image or icon displayed at the start of the row. Thumbnails help merchants visually identify items at a glance. Provide a URL to the image file.

  ```ts
  { url: string; }
  ```

### Badge

Configure the following properties on the badge component.

* color

  Controls the visual weight and emphasis of the badge. - \`base\`: Standard weight with moderate emphasis, suitable for most use cases. - \`strong\`: Increased visual weight for higher emphasis and prominence.

  ```ts
  "base" | "strong"
  ```

* icon

  An icon displayed inside the badge to provide additional visual context or reinforce the badge's meaning. Accepts any icon name from the icon library or a custom string identifier.

  ```ts
  "" | "replace" | "search" | "split" | "link" | "edit" | "info" | "incomplete" | "complete" | "product" | "variant" | "collection" | "select" | "color" | "money" | "order" | "code" | "adjust" | "affiliate" | "airplane" | "alert-bubble" | "alert-circle" | "alert-diamond" | "alert-location" | "alert-octagon" | "alert-octagon-filled" | "alert-triangle" | "alert-triangle-filled" | "app-extension" | "apps" | "archive" | "arrow-down" | "arrow-down-circle" | "arrow-down-right" | "arrow-left" | "arrow-left-circle" | "arrow-right" | "arrow-right-circle" | "arrow-up" | "arrow-up-circle" | "arrow-up-right" | "arrows-in-horizontal" | "arrows-out-horizontal" | "asterisk" | "attachment" | "automation" | "backspace" | "bag" | "bank" | "barcode" | "battery-low" | "bill" | "blank" | "blog" | "bolt" | "bolt-filled" | "book" | "book-open" | "bug" | "bullet" | "business-entity" | "button" | "button-press" | "calculator" | "calendar" | "calendar-check" | "calendar-compare" | "calendar-list" | "calendar-time" | "camera" | "camera-flip" | "caret-down" | "caret-left" | "caret-right" | "caret-up" | "cart" | "cart-abandoned" | "cart-discount" | "cart-down" | "cart-filled" | "cart-sale" | "cart-send" | "cart-up" | "cash-dollar" | "cash-euro" | "cash-pound" | "cash-rupee" | "cash-yen" | "catalog-product" | "categories" | "channels" | "chart-cohort" | "chart-donut" | "chart-funnel" | "chart-histogram-first" | "chart-histogram-first-last" | "chart-histogram-flat" | "chart-histogram-full" | "chart-histogram-growth" | "chart-histogram-last" | "chart-histogram-second-last" | "chart-horizontal" | "chart-line" | "chart-popular" | "chart-stacked" | "chart-vertical" | "chat" | "chat-new" | "chat-referral" | "check" | "check-circle" | "check-circle-filled" | "checkbox" | "chevron-down" | "chevron-down-circle" | "chevron-left" | "chevron-left-circle" | "chevron-right" | "chevron-right-circle" | "chevron-up" | "chevron-up-circle" | "circle" | "circle-dashed" | "clipboard" | "clipboard-check" | "clipboard-checklist" | "clock" | "clock-list" | "clock-revert" | "code-add" | "collection-featured" | "collection-list" | "collection-reference" | "color-none" | "compass" | "compose" | "confetti" | "connect" | "content" | "contract" | "corner-pill" | "corner-round" | "corner-square" | "credit-card" | "credit-card-cancel" | "credit-card-percent" | "credit-card-reader" | "credit-card-reader-chip" | "credit-card-reader-tap" | "credit-card-secure" | "credit-card-tap-chip" | "crop" | "currency-convert" | "cursor" | "cursor-banner" | "cursor-option" | "data-presentation" | "data-table" | "database" | "database-add" | "database-connect" | "delete" | "delivered" | "delivery" | "desktop" | "disabled" | "disabled-filled" | "discount" | "discount-add" | "discount-automatic" | "discount-code" | "discount-remove" | "dns-settings" | "dock-floating" | "dock-side" | "domain" | "domain-landing-page" | "domain-new" | "domain-redirect" | "download" | "drag-drop" | "drag-handle" | "drawer" | "duplicate" | "email" | "email-follow-up" | "email-newsletter" | "empty" | "enabled" | "enter" | "envelope" | "envelope-soft-pack" | "eraser" | "exchange" | "exit" | "export" | "external" | "eye-check-mark" | "eye-dropper" | "eye-dropper-list" | "eye-first" | "eyeglasses" | "fav" | "favicon" | "file" | "file-list" | "filter" | "filter-active" | "flag" | "flip-horizontal" | "flip-vertical" | "flower" | "folder" | "folder-add" | "folder-down" | "folder-remove" | "folder-up" | "food" | "foreground" | "forklift" | "forms" | "games" | "gauge" | "geolocation" | "gift" | "gift-card" | "git-branch" | "git-commit" | "git-repository" | "globe" | "globe-asia" | "globe-europe" | "globe-lines" | "globe-list" | "graduation-hat" | "grid" | "hashtag" | "hashtag-decimal" | "hashtag-list" | "heart" | "hide" | "hide-filled" | "home" | "home-filled" | "icons" | "identity-card" | "image" | "image-add" | "image-alt" | "image-explore" | "image-magic" | "image-none" | "image-with-text-overlay" | "images" | "import" | "in-progress" | "incentive" | "incoming" | "info-filled" | "inheritance" | "inventory" | "inventory-edit" | "inventory-list" | "inventory-transfer" | "inventory-updated" | "iq" | "key" | "keyboard" | "keyboard-filled" | "keyboard-hide" | "keypad" | "label-printer" | "language" | "language-translate" | "layout-block" | "layout-buy-button" | "layout-buy-button-horizontal" | "layout-buy-button-vertical" | "layout-column-1" | "layout-columns-2" | "layout-columns-3" | "layout-footer" | "layout-header" | "layout-logo-block" | "layout-popup" | "layout-rows-2" | "layout-section" | "layout-sidebar-left" | "layout-sidebar-right" | "lightbulb" | "link-list" | "list-bulleted" | "list-bulleted-filled" | "list-numbered" | "live" | "live-critical" | "live-none" | "location" | "location-none" | "lock" | "map" | "markets" | "markets-euro" | "markets-rupee" | "markets-yen" | "maximize" | "measurement-size" | "measurement-size-list" | "measurement-volume" | "measurement-volume-list" | "measurement-weight" | "measurement-weight-list" | "media-receiver" | "megaphone" | "mention" | "menu" | "menu-filled" | "menu-horizontal" | "menu-vertical" | "merge" | "metafields" | "metaobject" | "metaobject-list" | "metaobject-reference" | "microphone" | "microphone-muted" | "minimize" | "minus" | "minus-circle" | "mobile" | "money-none" | "money-split" | "moon" | "nature" | "note" | "note-add" | "notification" | "number-one" | "order-batches" | "order-draft" | "order-filled" | "order-first" | "order-fulfilled" | "order-repeat" | "order-unfulfilled" | "orders-status" | "organization" | "outdent" | "outgoing" | "package" | "package-cancel" | "package-fulfilled" | "package-on-hold" | "package-reassign" | "package-returned" | "page" | "page-add" | "page-attachment" | "page-clock" | "page-down" | "page-heart" | "page-list" | "page-reference" | "page-remove" | "page-report" | "page-up" | "pagination-end" | "pagination-start" | "paint-brush-flat" | "paint-brush-round" | "paper-check" | "partially-complete" | "passkey" | "paste" | "pause-circle" | "payment" | "payment-capture" | "payout" | "payout-dollar" | "payout-euro" | "payout-pound" | "payout-rupee" | "payout-yen" | "person" | "person-add" | "person-exit" | "person-filled" | "person-list" | "person-lock" | "person-remove" | "person-segment" | "personalized-text" | "phablet" | "phone" | "phone-down" | "phone-down-filled" | "phone-in" | "phone-out" | "pin" | "pin-remove" | "plan" | "play" | "play-circle" | "plus" | "plus-circle" | "plus-circle-down" | "plus-circle-filled" | "plus-circle-up" | "point-of-sale" | "point-of-sale-register" | "price-list" | "print" | "product-add" | "product-cost" | "product-filled" | "product-list" | "product-reference" | "product-remove" | "product-return" | "product-unavailable" | "profile" | "profile-filled" | "question-circle" | "question-circle-filled" | "radio-control" | "receipt" | "receipt-dollar" | "receipt-euro" | "receipt-folded" | "receipt-paid" | "receipt-pound" | "receipt-refund" | "receipt-rupee" | "receipt-yen" | "receivables" | "redo" | "referral-code" | "refresh" | "remove-background" | "reorder" | "replay" | "reset" | "return" | "reward" | "rocket" | "rotate-left" | "rotate-right" | "sandbox" | "save" | "savings" | "scan-qr-code" | "search-add" | "search-list" | "search-recent" | "search-resource" | "send" | "settings" | "share" | "shield-check-mark" | "shield-none" | "shield-pending" | "shield-person" | "shipping-label" | "shipping-label-cancel" | "shopcodes" | "slideshow" | "smiley-happy" | "smiley-joy" | "smiley-neutral" | "smiley-sad" | "social-ad" | "social-post" | "sort" | "sort-ascending" | "sort-descending" | "sound" | "sports" | "star" | "star-circle" | "star-filled" | "star-half" | "star-list" | "status" | "status-active" | "stop-circle" | "store" | "store-import" | "store-managed" | "store-online" | "sun" | "table" | "table-masonry" | "tablet" | "target" | "tax" | "team" | "text" | "text-align-center" | "text-align-left" | "text-align-right" | "text-block" | "text-bold" | "text-color" | "text-font" | "text-font-list" | "text-grammar" | "text-in-columns" | "text-in-rows" | "text-indent" | "text-indent-remove" | "text-italic" | "text-quote" | "text-title" | "text-underline" | "text-with-image" | "theme" | "theme-edit" | "theme-store" | "theme-template" | "three-d-environment" | "thumbs-down" | "thumbs-up" | "tip-jar" | "toggle-off" | "toggle-on" | "transaction" | "transaction-fee-add" | "transaction-fee-dollar" | "transaction-fee-euro" | "transaction-fee-pound" | "transaction-fee-rupee" | "transaction-fee-yen" | "transfer" | "transfer-in" | "transfer-internal" | "transfer-out" | "truck" | "undo" | "unknown-device" | "unlock" | "upload" | "variant-list" | "video" | "video-list" | "view" | "viewport-narrow" | "viewport-short" | "viewport-tall" | "viewport-wide" | "wallet" | "wand" | "watch" | "wifi" | "work" | "work-list" | "wrench" | "x" | "x-circle" | "x-circle-filled"
  ```

* size

  The size of the badge. - \`base\`: Default size suitable for most badge use cases. - \`large\`: Larger badge for increased visibility and prominence. - \`large-100\`: Extra large badge for maximum visibility in emphasized contexts.

  ```ts
  "base" | "large" | "large-100"
  ```

* tone

  The semantic meaning and color treatment of the component. - \`info\`: Informational content or helpful tips. - \`success\`: Positive outcomes or successful states. - \`warning\`: Important warnings about potential issues. - \`critical\`: Urgent problems or destructive actions. - \`auto\`: Automatically determined based on context. - \`neutral\`: General information without specific intent. - \`caution\`: Advisory notices that need attention.

  ```ts
  "info" | "success" | "warning" | "critical" | "auto" | "neutral" | "caution"
  ```

* setAttribute

  ```ts
  (name: string, value: string) => void
  ```

* attributeChangedCallback

  ```ts
  (name: string) => void
  ```

* connectedCallback

  ```ts
  () => void
  ```

* disconnectedCallback

  ```ts
  () => void
  ```

* adoptedCallback

  ```ts
  () => void
  ```

* queueRender

  A method that queues a run of the render function. You shouldn't need to call this manually - it should be handled by changes to

  ```ts
  () => void
  ```

* click

  A method like the standard \`element.click()\`, but you can influence the behavior with a \`sourceEvent\`. For example, if the \`sourceEvent\` was a middle click, or has particular keys held down, components will attempt to produce the desired behavior on links, such as opening the page in the background tab.

  ```ts
  ({ sourceEvent }?: ClickOptions) => void
  ```

### DataPoint

A single data point that can appear in a picker table cell. Can be text, a number, or undefined if the cell should be empty.

```ts
string | number | undefined
```

### Return payload

The picker returns a Promise that resolves to an object containing the `selected` property. Use this handle to await the merchant's selection result.

* **selected**

  **Promise\<string\[]>**

  A Promise that resolves with an array of selected item IDs when the merchant presses the **Select** button, or `undefined` if they cancel. Await this Promise to handle the selection result.

Examples

## Preview

![Select email templates. This example builds a custom picker for email templates with multiple columns and status badges. It defines column headers, populates items with searchable data fields, adds visual status indicators, and handles the selection promise. Use this pattern for app-specific resources like templates, product reviews, or subscription options.](https://shopify.dev/assets/assets/images/templated-apis-screenshots/admin/apis/picker-DqQDb5eA.png)

### Examples

* ####

  ##### Description

  Select email templates. This example builds a custom picker for email templates with multiple columns and status badges. It defines column headers, populates items with searchable data fields, adds visual status indicators, and handles the selection promise. Use this pattern for app-specific resources like templates, product reviews, or subscription options.

  ##### js

  ```js
  const picker = await shopify.picker({
    heading: 'Select a template',
    multiple: false,
    headers: [
      {content: 'Templates'},
      {content: 'Created by'},
      {content: 'Times used', type: 'number'},
    ],
    items: [
      {
        id: '1',
        heading: 'Full width, 1 column',
        data: ['Karine Ruby', '0'],
        badges: [{content: 'Draft', tone: 'info'}, {content: 'Marketing'}],
      },
      {
        id: '2',
        heading: 'Large graphic, 3 column',
        data: ['Charlie Mitchell', '5'],
        badges: [
          {content: 'Published', tone: 'success'},
          {content: 'New feature'},
        ],
        selected: true,
      },
      {
        id: '3',
        heading: 'Promo header, 2 column',
        data: ['Russell Winfield', '10'],
        badges: [{content: 'Published', tone: 'success'}],
      },
    ],
  });

  const selected = await picker.selected;
  ```

* ####

  ##### Description

  Limit selection count. This example limits selection to a maximum number of items by setting \`multiple: 2\` in the picker options. Use this when your feature has hard constraints, such as A/B test variants needing exactly two options, comparison views with fixed slots, or integration mappings that support a specific connection count.

  ##### js

  ```js
  const picker = await shopify.picker({
    heading: 'Select up to 2 templates',
    multiple: 2,
    headers: [{content: 'Template'}],
    items: [
      {
        id: '1',
        heading: 'Welcome email',
      },
      {
        id: '2',
        heading: 'Order confirmation',
      },
      {
        id: '3',
        heading: 'Shipping notification',
      },
    ],
  });

  const selected = await picker.selected;
  ```

* ####

  ##### Description

  Select unlimited items. This example allows unlimited selection by setting \`multiple: true\` without a numeric limit. Use this for bulk operations, mass notification sending, export tools, or tag management where selection quantity depends on merchant needs without artificial constraints.

  ##### js

  ```js
  const picker = await shopify.picker({
    heading: 'Select templates',
    multiple: true,
    headers: [{content: 'Template'}],
    items: [
      {
        id: '1',
        heading: 'Welcome email',
      },
      {
        id: '2',
        heading: 'Order confirmation',
      },
      {
        id: '3',
        heading: 'Shipping notification',
      },
    ],
  });

  const selected = await picker.selected;
  ```

* ####

  ##### Description

  Preselect items. This example opens the picker with items already selected by setting \`selected: true\` on individual items. Use this for edit workflows where you need to show what resources are already associated with a configuration. Merchants can modify the selection before confirming.

  ##### js

  ```js
  const picker = await shopify.picker({
    heading: 'Select templates',
    items: [
      {
        id: '1',
        heading: 'Welcome email',
        selected: true,
      },
      {
        id: '2',
        heading: 'Order confirmation',
      },
      {
        id: '3',
        heading: 'Shipping notification',
        selected: true,
      },
    ],
  });

  const selected = await picker.selected;
  ```

* ####

  ##### Description

  Disable specific items. This example disables specific picker items to prevent selection while keeping them visible for context. Set \`disabled: true\` on individual items to mark them as non-selectable. Use this for showing all available options while preventing selection of incompatible resources or deprecated features.

  ##### js

  ```js
  const picker = await shopify.picker({
    heading: 'Select a template',
    items: [
      {
        id: '1',
        heading: 'Welcome email',
        disabled: true,
      },
      {
        id: '2',
        heading: 'Order confirmation',
      },
      {
        id: '3',
        heading: 'Shipping notification',
      },
    ],
  });

  const selected = await picker.selected;
  ```

* ####

  ##### Description

  Use GraphQL data. This example populates the picker with data from the GraphQL Admin API. It fetches order data, maps results to picker items with badges showing fulfillment and payment status, and opens the picker with the returned data. Use this pattern for Shopify data not available through the Resource Picker API, such as orders, draft orders, or fulfillments.

  ##### js

  ```js
  const response = await fetch('shopify:admin/api/graphql.json', {
    method: 'POST',
    body: JSON.stringify({
      query: `
        query GetOrders($first: Int!) {
          orders(first: $first) {
            edges {
              node {
                id
                name
                customer {
                  displayName
                }
                originalTotalPriceSet {
                  shopMoney {
                    amount
                  }
                }
                displayFulfillmentStatus
                displayFinancialStatus
              }
            }
          }
        }
      `,
      variables: {first: 10},
    }),
  });

  const {data} = await response.json();
  const orders = data.orders.edges;

  const picker = await shopify.picker({
    heading: 'Select orders',
    multiple: true,
    headers: [
      {content: 'Order'},
      {content: 'Customer'},
      {content: 'Total', type: 'number'},
    ],
    items: orders.map(({node: order}) => ({
      id: order.id,
      heading: order.name,
      data: [order.customer.displayName, `$${order.originalTotalPriceSet.shopMoney.amount}`],
      badges: [
        {
          content: order.displayFulfillmentStatus,
          tone: order.displayFulfillmentStatus === 'FULFILLED' ? 'success' : 'warning',
          progress: order.displayFulfillmentStatus === 'FULFILLED' ? 'complete' : 'incomplete',
        },
        {
          content: order.displayFinancialStatus,
          tone: order.displayFinancialStatus === 'PAID' ? 'success' : 'warning',
          progress: order.displayFinancialStatus === 'PAID' ? 'complete' : 'incomplete',
        },
      ],
    })),
  });

  const selected = await picker.selected;
  ```

***
