HorizontalList
HorizontalList is a row of choices that scrolls sideways: a segmented control by default, or a row of chips. When the user taps the item at the edge and more items are hidden past it, the list scrolls a little so the next item comes into view.
import { HorizontalList } from '@elik56a/react-native-pickers';
<HorizontalList items={views} value={view} onChange={setView} accessibilityLabel="Calendar view" />
Segmented controls
A few items share the track's width. With more than fit, the track scrolls, and the thumb follows the selection along it.
<View style={{ gap: 16 }}> <HorizontalList items={sorts} value={sort} onChange={setSort} accessibilityLabel="Sort by" /> <HorizontalList items={ranges} value={range} onChange={setRange} accessibilityLabel="Chart range" /> </View>
Chips
variant="chips" shows separate pills. With more items than fit, tap the last visible one: the list scrolls to show the next.
<HorizontalList variant="chips" items={cuisines} value={cuisine} onChange={setCuisine} accessibilityLabel="Cuisine" />
Scroll arrows
showArrows adds round ‹ › buttons that scroll the list about a page. They're off by default: swiping already scrolls on touch screens, so arrows help most with a mouse on the web, or a long row of chips.
By default (arrowPosition="outside") the arrows sit beside the list and show while its items overflow; the one that can't scroll further is dimmed, so the list keeps its width.
<HorizontalList variant="chips" items={countries} value={country} onChange={setCountry} showArrows accessibilityLabel="Country" />
Over the edges
arrowPosition="overlay" floats the arrows over the list's edges instead, each shown only while items are hidden past it, so the list keeps its full width.
<HorizontalList variant="chips" items={topics} value={topic} onChange={setTopic} showArrows arrowPosition="overlay" accessibilityLabel="Topic" />
Custom arrows
renderArrow(side, disabled) replaces the ‹ › glyph, and the arrow slot styles the button around it.
<HorizontalList variant="chips" items={genres} value={genre} onChange={setGenre} showArrows renderArrow={(side, disabled) => ( <Text style={{ fontSize: 16, fontWeight: '700', color: disabled ? theme.colors.textDisabled : theme.colors.primary }}> {side === 'left' ? '←' : '→'} </Text> )} styles={{ arrow: { backgroundColor: theme.colors.primaryMuted, borderWidth: 0, boxShadow: 'none' } }} accessibilityLabel="Genre" />
Icons
Give an item an icon and choose its side with iconPosition: 'left' (the default), 'right', 'top' or 'bottom'. 'left' and 'right' follow the layout direction, so they swap in RTL. An icon can also be a function of { selected, disabled, color }, to color it like its label.
<HorizontalList items={modes} value={mode} onChange={setMode} iconPosition="top" accessibilityLabel="Travel mode" />
Icons can be any view. Here each color filter has a swatch on its left:
<HorizontalList variant="chips" items={colors} value={color} onChange={setColor} accessibilityLabel="Color" />
Disabled items
A disabled item is dimmed and can't be chosen, like sizes that are sold out.
<HorizontalList variant="chips" items={sizes} value={size} onChange={setSize} accessibilityLabel="Size" />
Any value type
Values can be anything: numbers, ids, objects. HorizontalList<T> infers T from items, so onChange gets the typed value.
<View style={{ gap: 12 }}> <HorizontalList items={tips} value={tip} onChange={setTip} accessibilityLabel="Tip" /> <Text style={{ color: theme.colors.text, fontSize: 16 }}>Total: ${(bill * (1 + tip / 100)).toFixed(2)}</Text> </View>
Object values are compared with Object.is. When they're recreated (say, refetched from a server), pass isEqual:
<HorizontalList items={plans} value={plan} onChange={setPlan} isEqual={(a, b) => a.id === b.id} />
Custom items
renderItem draws each item's content, and the item slot resizes the pill around it. Here, a strip of the next three weeks:
<View style={{ alignItems: 'center', gap: 2 }}> <Text style={{ fontSize: 11, color: selected ? theme.colors.primary : theme.colors.textSecondary }}> {item.value === 0 ? 'Today' : date.toLocaleDateString(undefined, { weekday: 'short' })} </Text> <Text style={{ fontSize: 18, fontWeight: '700', color }}>{date.getDate()}</Text> </View> ); }} accessibilityLabel="Day" />
Try the props
varianticonPositionarrowPosition<HorizontalList items={modes} value={mode} onChange={setMode} />
Behaviour
- Controlled: pass
valueand update it inonChange. Tapping the selected item callsonChangeagain. - In
segmented, the thumb slides to the selected item; items share the track's width while they fit, and scroll when they don't. - Tapping an item whose neighbor is hidden scrolls that neighbor into view, plus a little of the one after it, so the user sees there's more.
- With
showArrows, the arrows need a frame around the list:stylelays out that frame andstyles.containerstyles the list itself.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | ReadonlyArray<HorizontalListItem<T>> | required | { label, value, disabled?, icon? }. icon is a node, or a function of { selected, disabled, color }. |
value | T | null | — | Selected value. |
onChange | (value: T, item) => void | — | Called on tap. |
variant | 'segmented' | 'chips' | 'segmented' | Track with a sliding thumb, or separate pills. |
iconPosition | 'left' | 'right' | 'top' | 'bottom' | 'left' | Where each icon sits around its label. |
showArrows | boolean | false | Round ‹ › buttons that scroll about a page, shown while the items overflow. |
arrowPosition | 'outside' | 'overlay' | 'outside' | Beside the list, the one that can't scroll further dimmed; or over its edges, each shown while items hide past it. |
renderArrow | (side: 'left' | 'right', disabled: boolean) => ReactNode | — | Custom arrow content. |
isEqual | (a: T, b: T) => boolean | Object.is | Value equality. |
renderItem | (info: { item, selected, disabled, theme }) => ReactNode | — | Custom item content. |
accessibilityLabel | string | — | Names the list for screen readers. |
style | StyleProp<ViewStyle> | — | Container style. |
styles | SlotStyles<HorizontalListSlot> | — | Slots below. |
theme | DeepPartial<PickerTheme> | — | Theme override. |
design | 'ios' | 'material' | 'web' | from PickersProvider | The look to follow, over the provider's: iOS, Material or web. |
testID | string | — | Test ID; items get {testID}-item-{index}. |
Slots
| Slot | Element |
|---|---|
container | The scroll view (the track in segmented) |
content | The row inside it |
item / selectedItem | Each item / the selected one |
itemLabel / selectedItemLabel | Item label |
thumb | The sliding thumb (segmented) |
arrow / arrowText | Each scroll arrow / its glyph (showArrows) |
Accessibility
The list is a radiogroup named by accessibilityLabel; items are radios with checked and disabled states. Scroll arrows are buttons labelled "Scroll left" and "Scroll right".
Related
- Category filter chips: chips filtering a menu
- Filters bottom sheet: a segmented sort order inside a sheet
- Flight search form: trip type and cabin in a booking form