---
title: Migrate Listbox from Polaris React
description: >-
  Replace Polaris React Listbox with a select, choice list, Picker API, Resource
  Picker API, or complete searchable resource pattern.
source_url:
  html: >-
    https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/listbox
  md: >-
    https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/listbox.md
api_name: app-home
---

# Migrate Listbox from Polaris React

Polaris web components don't have a general-purpose listbox primitive. Choose a supported control or pattern instead of rebuilding Polaris React `Listbox` one subcomponent at a time.

When `Listbox` is nested inside Polaris React `Combobox`, migrate the pair as one interaction. Follow the [Combobox migration](https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/combobox) first; don't replace the listbox separately while leaving the old combobox responsible for focus, input, and open state.

| Existing Listbox | Polaris web components | Migration type |
| - | - | - |
| Compact single selection | `s-select` and `s-option` | Direct |
| Visible single or multiple choice | `s-choice-list` and `s-choice` | Change pattern |
| Shopify products, product variants, or collections | [Resource Picker API](https://shopify.dev/docs/api/app-home/apis/user-interface-and-interactions/resource-picker-api) | App Bridge |
| Other Shopify or app-specific resources | [Picker API](https://shopify.dev/docs/api/app-home/apis/user-interface-and-interactions/picker-api), or search plus a resource-list or index pattern | App Bridge or compose |
| Action commands | A trigger and `s-menu` | Change pattern |

***

## Migrate finite options

## Migrating a selectable option list

##### Polaris web components

```tsx
function CheckoutOptions({selected, toggleOption}) {
  return (
    <s-choice-list
      label="Checkout options"
      labelAccessibilityVisibility="exclusive"
      name="checkout"
      values={selected}
      multiple
      onChange={(event) => toggleOption(event.currentTarget.values)}
    >
      <s-choice value="shipping">Use shipping address for billing</s-choice>
      <s-choice value="confirmation">Require a confirmation step</s-choice>
    </s-choice-list>
  );
}
```

##### Polaris React

```tsx
import {Listbox} from '@shopify/polaris';

<Listbox allowMultiple onSelect={toggleOption}>
  <Listbox.Option value="shipping" selected={selected.includes('shipping')}>
    Use shipping address for billing
  </Listbox.Option>
  <Listbox.Option value="confirmation" selected={selected.includes('confirmation')}>
    Require a confirmation step
  </Listbox.Option>
</Listbox>
```

***

## Replace Listbox responsibilities

Map option `value`, label, disabled state, and supporting text to explicit destination children. Put the group label, selected values, required state, and error on the containing select or choice list.

Don't carry `Listbox.Option`, `Listbox.Action`, `Listbox.Section`, or `Listbox.Loading` into a compatibility component. Actions belong in a menu or visible buttons; sections of complex results usually indicate a resource pattern; remote loading belongs to the query that owns results.

For remote results, implement search, cancellation or stale-response protection, loading, empty, error, pagination, selected IDs, and create-new behavior together. Use the debounce and `AbortController` pattern in the [Filters migration](https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/filters#debounce-and-cancel-remote-requests), then render results with the [resource list pattern](https://shopify.dev/docs/api/app-home/patterns/compositions/resource-list) or [index table pattern](https://shopify.dev/docs/api/app-home/patterns/compositions/index-table). Don't assemble an `s-popover` with buttons and call it a listbox unless the custom implementation supplies complete focus, selection, typeahead, and keyboard semantics.

***

## Test the migration

* Select, deselect, restore, and submit stable values.
* Verify single-select controls never retain multiple values.
* Exercise disabled options and group errors.
* Test rapid remote search through loading, empty, failure, and stale responses.
* Verify keyboard behavior and accessible group and option names.

***

## Remove Polaris React

Remove `Listbox`, its subcomponent imports, option descriptors, and custom context once the complete workflow uses the destination control or pattern. Remove `@shopify/polaris` only after no other route in scope imports it.

***

## Related guidance

* [Migrate OptionList from Polaris React](https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/option-list)
* [Migrate Combobox from Polaris React](https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/combobox)
* [Resource list pattern](https://shopify.dev/docs/api/app-home/patterns/compositions/resource-list)
* [Index table pattern](https://shopify.dev/docs/api/app-home/patterns/compositions/index-table)
* [Picker API](https://shopify.dev/docs/api/app-home/apis/user-interface-and-interactions/picker-api)
* [Resource Picker API](https://shopify.dev/docs/api/app-home/apis/user-interface-and-interactions/resource-picker-api)

***
