Vitus Labs
Elements

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

PropTypeDescription
childrenReactNodeReact children (takes priority over data)
dataany[]Array of data items to render
componentComponentTypeComponent to render each item
valueNamestringProp name for simple array items (strings/numbers)
itemKeystring | ((item, index) => string)Key derivation for each item
itemPropsobject | (extendedProps) => objectExtra props for each item
wrapComponentComponentTypeOptional wrapper around each item
wrapPropsobject | (extendedProps) => objectProps for the wrapper
rootElementbooleanWrap list in Element container (default: false)

When rootElement={true}, all Element props are also available (tag, direction, gap, alignX, alignY, css, etc.).

Extended Item Props

Each rendered item receives these additional props:

PropTypeDescription
indexnumber0-based position
positionnumber1-based position
firstbooleantrue if first item
lastbooleantrue if last item
oddbooleantrue if position is odd (1, 3, 5...)
evenbooleantrue 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 string or number (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

  1. children — If provided, children are rendered (data/component ignored)
  2. 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

On this page