---
title: List
description: Render caller-defined rows with optional virtual scrolling.
package: moraine
version: 0.6.0
repository: https://github.com/subframe7536/moraine
---

# List

> Render caller-defined rows with optional virtual scrolling.

Use List when the caller owns each row’s markup and needs a stable collection surface. Pass `items` and an `itemRender` component; List does not take row content through `children`. Add a virtual renderer for large collections only when ordinary rendering becomes costly.

## Basic usage

```tsx
import { List } from 'moraine'

export function Example() {
  return <List items={['Alpha', 'Beta']} itemRender={(item) => <li>{item.item}</li>} />
}
```

## Anatomy

```text
List [component; slot=root; <ul>]
```

## Usage

### Row ownership and virtualization

`itemRender` receives the item, its index, and row props. Apply those props to the final row element when using `virtualRender`; the virtualizer needs them for positioning and measurement. The default root is a `<ul>` with `role="list"`, so render list-item semantics in each row or choose a different root with `as` for another structure. TypeScript infers the item and root element types from `items` and `as`.

`createListVirtualizer` is an optional adapter from `moraine/virtualizer` and needs `@tanstack/virtual-core`. The visible row count and scroll behavior depend on your estimated or measured row sizes.

Read the [Virtualization guide](https://moraine.subf.dev/docs/virtualization.md) for setup, row measurements, stable keys, and keyboard navigation.

```tsx
import { List } from 'moraine'
import { createListVirtualizer } from 'moraine/virtualizer'

export function Virtualization() {
  const ITEMS = Array.from({ length: 10_000 }, (_, index) => ({
    id: index + 1,
    label: `Result ${index + 1}`,
  }))

  type Item = (typeof ITEMS)[number]

  const virtualizer = createListVirtualizer<Item, HTMLElement, HTMLDivElement>({
    estimateSize: () => 36,
    getItemKey: (item) => item.id,
    overscan: 8,
  })

  return (
    <List
      as="div"
      items={ITEMS}
      virtualRender={virtualizer.virtualRender}
      role="list"
      aria-label="Virtual results"
      class="border border-border rounded-md h-72 w-full overflow-y-auto"
      itemRender={(context) => (
        <div {...context.props} role="listitem">
          <div class="px-3 border-b border-border flex h-9 items-center">{context.item.label}</div>
        </div>
      )}
    />
  )
}
```

## Examples

### Dynamic heights

```tsx
import { List } from 'moraine'
import { createListVirtualizer } from 'moraine/virtualizer'
import { For } from 'solid-js'

export function DynamicHeight() {
  const ITEMS = Array.from({ length: 1_000 }, (_, index) => ({
    id: index + 1,
    label: `Result ${index + 1}`,
    details: Array.from(
      { length: (index % 4) + 1 },
      (_, detailIndex) => `Detail line ${detailIndex + 1} for result ${index + 1}.`,
    ),
  }))

  type Item = (typeof ITEMS)[number]

  const virtualizer = createListVirtualizer<Item, HTMLElement, HTMLDivElement>({
    estimateSize: () => 96,
    getItemKey: (item) => item.id,
    gap: 8,
    overscan: 8,
  })

  return (
    <List
      as="div"
      items={ITEMS}
      virtualRender={virtualizer.virtualRender}
      role="list"
      aria-label="Variable-height results"
      class="py-2 border border-border rounded-md h-80 w-full overflow-y-auto"
      itemRender={(context) => (
        <div {...context.props} role="listitem">
          <div class="mx-2 px-3 py-2 border border-border rounded-md">
            <div class="font-medium">{context.item.label}</div>
            <div class="text-sm text-muted-foreground">
              <For each={context.item.details}>
                {(detail) => <span class="block">{detail}</span>}
              </For>
            </div>
          </div>
        </div>
      )}
    />
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| fallback | JSX.Element \| undefined | — | Content rendered inside the list when the collection is empty. |
| itemRender* | Component<ItemRenderProps<TItem, TItemElement>> | — | Renders one collection item. |
| items | readonly TItem[] \| undefined | — | Reactive collection rendered by the list. |
| virtualRender | Component<VirtualRenderProps<TItem, HTMLElement, TItemElement>> \| undefined | — | Replaces normal iteration with caller-controlled virtual rendering. |
| as | T \| undefined | 'ul' | Root element or component. |
| class | SlotClassValue \| undefined | — | Class applied to the component root or trigger element. |
| style | SlotStyleValue \| undefined | — | Style applied to the component root or trigger element. |
