Organism
SearchList
Requires <UIProvider> — Getting Started →
Installation
Install just this component (copies the source into your project, no npm dependency)
npx orn-ui add search-listOr install the whole package and import it
pnpm add orn-ui
import { SearchList } from 'orn-ui/search-list';Usage
- when to use — A list whose main entry point is a search field. Otherwise, List.
- searchValue / onSearchChange — Controlled: debounce and query live in your screen, not in the component.
- loadingMode — `replace` swaps the list for a spinner; `overlay` keeps the layout, which avoids the jump while typing.
- onScanPress — Adds the scan action next to the field — for codes, QR, barcodes.
- hasMore / onLoadMore — Pagination with an explicit end, so the footer says when there is nothing left.
Demo clip not recorded yet — see MEDIA.md
Variants
Search + results
<SearchList
searchValue={query}
onSearchChange={setQuery}
data={data.map((name) => ({ name }))}
keyExtractor={(item) => item.name}
renderItem={({ item }) => (
<Card style={{ marginBottom: 8 }}>
<Body>{item.name}</Body>
</Card>
)}
emptyTitle="No matches"
/>With scan action
<SearchList
searchValue=""
onSearchChange={() => {}}
onScanPress={() => {}}
data={[]}
keyExtractor={(item: any) => item.name}
renderItem={() => null}
emptyTitle="Scan a barcode or search"
/>Full demo source
const variants: VariantDef[] = [
{
label: 'Search + results',
content: (
<SearchList
searchValue={query}
onSearchChange={setQuery}
data={data.map((name) => ({ name }))}
keyExtractor={(item) => item.name}
renderItem={({ item }) => (
<Card style={{ marginBottom: 8 }}>
<Body>{item.name}</Body>
</Card>
)}
emptyTitle="No matches"
/>
),
},
{
label: 'With scan action',
content: (
<SearchList
searchValue=""
onSearchChange={() => {}}
onScanPress={() => {}}
data={[]}
keyExtractor={(item: any) => item.name}
renderItem={() => null}
emptyTitle="Scan a barcode or search"
/>
),
},
];
return <VariantList variants={variants} fill />;Props
| Name | Type | Default | Description |
|---|---|---|---|
header? | ReactNode | — | Optional chrome above the search field (e.g. back button + title). |
searchValue | string | — | — |
onSearchChange | (text: string) => void | — | — |
searchPlaceholder? | string | — | — |
searchIsLoading? | boolean | false | — |
onScanPress? | () => void | — | — |
scanAccessibilityLabel? | string | Scan | — |
extraActions? | ReactNode | — | — |
data | T[] | — | — |
keyExtractor | (item: T, index: number) => string | — | — |
renderItem | ListRenderItem<T> | — | — |
isLoading? | boolean | false | Initial load: while true and there's no data, placeholders are shown. |
loadingMode? | 'replace' | 'overlay' | replace | 'replace' swaps the whole list for a spinner; 'overlay' shows it on top, keeping the layout. |
isRefreshing? | boolean | false | Reload with data already on screen (pull-to-refresh). |
isLoadingMore? | boolean | false | — |
isReady? | boolean | true | Controlled by the consumer: while false, a fullscreen spinner is shown. |
loadingText? | string | — | — |
loadingMoreText? | string | — | — |
hasMore? | boolean | false | — |
noMoreText? | string | — | — |
skeletonCount? | number | 6 | Ghost rows for the initial load. |
renderSkeletonItem? | () => ReactElement<unknown, string | JSXElementConstructor<any>> | — | Custom placeholder, to match the real shape of the item. |
onLoadMore? | () => void | — | — |
onEndReached? | () => void | — | — |
onEndReachedThreshold? | number | 0.3 | — |
onRefresh? | () => void | — | — |
emptyTitle? | string | — | — |
emptyDescription? | string | — | — |
emptyIconName? | IconName | — | — |
ListHeaderComponent? | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentType<any> | — | — |
ListFooterComponent? | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentType<any> | — | — |
contentContainerStyle? | StyleProp<ViewStyle> | — | — |
containerStyle? | ViewStyle | — | — |
ListComponent? | ComponentType<any> | FlatList | List component to use (FlashList, etc). |
listProps? | Record<string, unknown> | — | — |