Skip to main content

ListPicker

Selection modal for choosing from a list of options with optional search.

Basic Usage​

import { ListPicker } from '@multinaire/expo-ui';

<ListPicker
type="dropdown"
items={[
{ value: 'us', text: 'United States' },
{ value: 'uk', text: 'United Kingdom' },
{ value: 'ca', text: 'Canada' },
]}
selectedItem={selectedCountry}
onChange={(item) => setSelectedCountry(item)}
/>

Props​

PropTypeDefaultDescription
type'list' | 'dropdown'—Presentation style of the picker. 'list' shows items inline; 'dropdown' shows them in a modal
itemsListPickerItemData[]—Array of selectable items
selectedItemT—Currently selected item value
isLoadingbooleanfalseShow loading skeleton items in place of the list
searchInputPlaceholderstring—Placeholder text for the search input
searchPredicate(item, filter) => boolean—Custom filter predicate. Return true to keep the item visible for a given filter string
onChangeSearch(filter: string) => void—Callback invoked on every keystroke in the search input
onChange(item: T) => void—Callback invoked with the newly selected item value
onSearch(filter: string) => void—Callback invoked when the user submits a search query

ListPickerItemData​

PropertyTypeDescription
valueTThe value associated with this item, passed to onChange when selected
textstringPrimary label text of the item. Rendered on a single line — longer text is truncated with an ellipsis
leadingIconName | ImageSourceLeading icon name or image source displayed at the start of the item

ListPickerItem​

The row ListPicker renders for each item, exported for use on its own — as a list row elsewhere, or as the row of a SortableContainer, whose default itemHeight is exactly this row's height.

PropTypeDefaultDescription
textstring—Primary label text of the item. Rendered on a single line — longer text is truncated with an ellipsis
leadingIconName | ImageSource—Leading icon name or image source displayed at the start of the item
selectedbooleanfalseWhether the item reads as selected. Adds a trailing checkmark and a highlighted border
testIDstringtextTest identifier for UI automation
onPress() => void—Callback invoked when the item is pressed. Omit it to render a non-interactive row — the row no longer dims on touch, and a surrounding gesture (a SortableContainer drag, for instance) receives the touch uncontested

Examples​

<ListPicker
type="dropdown"
items={countries.map((c) => ({
value: c.code,
text: c.name,
leading: { uri: c.flagUrl },
}))}
selectedItem={country}
onChange={setCountry}
/>
<ListPicker
type="list"
items={users.map((u) => ({
value: u.id,
text: u.name,
leading: { uri: u.avatarUrl },
}))}
selectedItem={selectedUser}
searchInputPlaceholder="Search users..."
searchPredicate={(item, filter) =>
item.text.toLowerCase().includes(filter.toLowerCase())
}
onChange={setSelectedUser}
/>

With Icons​

<ListPicker
type="dropdown"
items={[
{ value: 'card', text: 'Credit Card', leading: 'CreditCard' },
{ value: 'paypal', text: 'PayPal', leading: 'Wallet' },
{ value: 'bank', text: 'Bank Transfer', leading: 'Building' },
]}
selectedItem={paymentMethod}
onChange={setPaymentMethod}
/>

With Loading State​

<ListPicker
type="list"
isLoading={isLoadingOptions}
items={options}
selectedItem={selected}
onChange={setSelected}
/>
<ListPicker
type="list"
items={searchResults}
selectedItem={selected}
searchInputPlaceholder="Search..."
onSearch={(query) => fetchSearchResults(query)}
onChange={setSelected}
/>

Notes​

  • Each row is a fixed-height Card, and its label is capped at one line — long text truncates with an ellipsis rather than wrapping, so rows stay aligned with each other and with their leading icon and selected checkmark.