Organism
List
Requires <UIProvider> — Getting Started →
Installation
Install just this component (copies the source into your project, no npm dependency)
npx orn-ui add listOr install the whole package and import it
pnpm add orn-ui
import { List } from 'orn-ui/list';Usage
- when to use — Any data list: it bundles loading, empty and pagination so each screen does not reinvent them.
- isLoading / isReady — `isLoading` is the data fetch; `isReady` is everything the screen needs before rendering at all.
- emptyTitle / emptyDescription — Feed the EmptyState it renders when there is nothing to show.
- onEndReached / isLoadingMore — Pagination: the callback loads, the flag shows the footer spinner.
- ListComponent — `FlatList` by default; swap in FlashList without touching the rest.
Demo clip not recorded yet — see MEDIA.md
Variants
Loaded
<List
data={DATA}
keyExtractor={(item) => item.id}
isLoading={false}
renderItem={({ item }) => (
<Card style={{ marginBottom: 8 }}>
<Body>{item.name}</Body>
</Card>
)}
/>Loading
<List data={[]} keyExtractor={(i: any) => i.id} isLoading loadingText="Fetching items..." renderItem={() => null} />Empty
<View style={{ flex: 1 }}>
<Button
title={showEmpty ? 'Reset' : 'Simulate empty result'}
variant="outline"
onPress={() => setShowEmpty((v) => !v)}
style={{ marginBottom: 12 }}
/>
<List
data={showEmpty ? [] : DATA.slice(0, 3)}
keyExtractor={(item: any) => item.id}
isLoading={false}
emptyTitle="No items"
emptyDescription="Try a different filter"
renderItem={({ item }: any) => (
<Card style={{ marginBottom: 8 }}>
<Body>{item.name}</Body>
</Card>
)}
/>
</View>Full demo source
const variants: VariantDef[] = [
{
label: 'Loaded',
content: (
<List
data={DATA}
keyExtractor={(item) => item.id}
isLoading={false}
renderItem={({ item }) => (
<Card style={{ marginBottom: 8 }}>
<Body>{item.name}</Body>
</Card>
)}
/>
),
},
{
label: 'Loading',
content: <List data={[]} keyExtractor={(i: any) => i.id} isLoading loadingText="Fetching items..." renderItem={() => null} />,
},
{
label: 'Empty',
content: (
<View style={{ flex: 1 }}>
<Button
title={showEmpty ? 'Reset' : 'Simulate empty result'}
variant="outline"
onPress={() => setShowEmpty((v) => !v)}
style={{ marginBottom: 12 }}
/>
<List
data={showEmpty ? [] : DATA.slice(0, 3)}
keyExtractor={(item: any) => item.id}
isLoading={false}
emptyTitle="No items"
emptyDescription="Try a different filter"
renderItem={({ item }: any) => (
<Card style={{ marginBottom: 8 }}>
<Body>{item.name}</Body>
</Card>
)}
/>
</View>
),
},
];
return <VariantList variants={variants} fill />;Props
| Name | Type | Default | Description |
|---|---|---|---|
data | T[] | — | — |
keyExtractor | (item: T, index: number) => string | — | — |
renderItem | ListRenderItem<T> | — | — |
isLoading | boolean | — | Initial load: while true and there's no data, placeholders are shown. |
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 full-screen spinner is shown. |
loadingText? | string | — | — |
loadingMoreText? | 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. |
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? | ViewStyle | — | — |
containerStyle? | ViewStyle | — | — |
ListComponent? | ComponentType<any> | FlatList | List component to use (FlashList, etc). |
listProps? | Record<string, unknown> | — | — |