List
Data-driven list renderer with extended item props and optional Element wrapper.
List is a data-driven component that renders arrays of items with a specified component. Each item receives extended props with positional metadata (index, first, last, odd, even).
Basic Usage
import { List } from '@vitus-labs/elements'
// Simple string array
<List
component={({ children }) => <li>{children}</li>}
data={['Apple', 'Banana', 'Cherry']}
valueName="children"
/>
// Object array
<List
component={({ name, email }) => (
<div>{name} ({email})</div>
)}
data={[
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Bob', email: 'bob@example.com' },
]}
itemKey="name"
/>
// React children (highest priority)
<List>
<li>Item 1</li>
<li>Item 2</li>
</List>Props
List's props are typed via a generic Props<T> that discriminates on the element type of data — TypeScript narrows the prop shape automatically based on what you pass to data.
| Prop | Type | Description |
|---|---|---|
children | ReactNode | React children (takes priority over data) |
data | Array<T | null | undefined> | Array of data items. T is inferred at the call site from this prop. |
component | ComponentType | Component to render each item |
valueName | string | Prop name for primitive items. Required when T is string | number; forbidden when T is an object. |
itemKey | T-aware | For primitive T: (item: T, index: number) => string | number. For object T: keyof T | ((item: T, index) => string | number). |
itemProps | object | ((item, ext) => object) | Extra props merged onto each item, optionally per-item. Callback gets T-shaped item. |
wrapComponent | ComponentType | Optional wrapper around each item |
wrapProps | object | ((item, ext) => object) | Props merged onto the wrapper, optionally per-item |
rootElement | boolean | Wrap list in Element container (default: false) |
When rootElement={true}, all Element props are also available (tag, direction, gap, alignX, alignY, css, etc.).
Mode discrimination
data element type | Branch | valueName | itemKey element type |
|---|---|---|---|
string / number | SimpleProps<T> | required | (item: T, index) => string | number |
| object | ObjectProps<T> | forbidden (never) | keyof T | ((item: T, index) => …) |
(no data, just children) | ChildrenProps | forbidden | n/a |
| (no narrowing — generic call site) | LooseProps | optional | union of all above |
Without an explicit generic argument and without TS being able to infer T from data, the loose backwards-compatible shape is used — same surface as before the generic was introduced. Concrete data arrays narrow automatically.
Extended Item Props
Each rendered item receives these additional props:
| Prop | Type | Description |
|---|---|---|
index | number | 0-based position |
position | number | 1-based position |
first | boolean | true if first item |
last | boolean | true if last item |
odd | boolean | true if position is odd (1, 3, 5...) |
even | boolean | true if position is even (2, 4, 6...) |
With Root Element
Wrap the list in an Element container for layout control:
<List
rootElement
component={Card}
data={products}
gap={16}
direction="rows"
alignX="spaceBetween"
tag="nav"
css={(css) => css`padding: 24px;`}
/>Without rootElement (default): Items render as a fragment.
With rootElement: Items render inside an Element with full layout props.
Item Props Callback
Pass dynamic props to each item based on its position:
<List
component={Card}
data={items}
itemProps={({ index, first, last, odd }) => ({
highlighted: odd,
className: first ? 'first-item' : last ? 'last-item' : '',
style: { animationDelay: `${index * 100}ms` },
})}
/>Wrapper Component
Wrap each item in a container:
<List
component={Item}
data={items}
wrapComponent={({ children, ...props }) => (
<div className="item-wrapper" {...props}>{children}</div>
)}
wrapProps={({ odd }) => ({
className: odd ? 'odd-row' : 'even-row',
})}
/>Data Modes
List classifies data arrays internally:
- Simple — All items are
stringornumber(nullish items filtered out) - Complex — All items are objects
- Mixed — Returns
null(invalid, won't render)
Simple Arrays
For arrays of primitives, use valueName to specify which prop receives the value:
<List
component={Tag}
data={['React', 'TypeScript', 'CSS']}
valueName="label"
/>
// Each Tag receives: { label: 'React' }, { label: 'TypeScript' }, etc.Object Arrays
Object properties are spread directly onto the component:
<List
component={UserRow}
data={[
{ id: 1, name: 'Alice', role: 'Admin' },
{ id: 2, name: 'Bob', role: 'User' },
]}
itemKey="id"
/>
// UserRow receives: { id: 1, name: 'Alice', role: 'Admin', index: 0, ... }Per-Item Component Override
In object arrays, each item can specify its own component via a component property:
<List
component={DefaultCard}
data={[
{ id: 1, name: 'Alice', component: SpecialCard },
{ id: 2, name: 'Bob' }, // uses DefaultCard
]}
itemKey="id"
/>The component property is consumed by the iterator and not passed to the rendered component.
Rendering Priority
children— If provided, children are rendered (data/component ignored)data+component— Data-driven rendering
When children are used, each child receives extended props (index, first, last, etc.) and can be wrapped with wrapComponent.
Without rootElement (Default)
Items render as a flat fragment — no wrapping <div>:
<List component={Item} data={items} />
// Renders: <Item /><Item /><Item /> (no wrapper)Only Iterator-specific props (children, component, data, itemKey, valueName, itemProps, wrapComponent, wrapProps) are used. Element layout props are ignored.
API Reference
Prop
Type