Skip to content

List

A flexible component for rendering lists with optional virtualization support.

Live Demo

Scroll through 10,000 items with smooth virtualization:

The List component renders lists efficiently. By default, it renders all items. For large datasets, enable the virtual option to render only visible items using virtualization.

When to Use List

Simple List (default):

  • Small to medium lists (< 100 items)
  • When you need all items in the DOM
  • For lists that don't change frequently

Virtual List (virtual={true}):

  • Large lists with hundreds or thousands of items
  • When performance is critical
  • When memory efficiency is important

Usage

Simple List (Default)

tsx
import { List } from 'flexium/primitives';
import { state } from 'flexium/core';

function App() {
  const items = state([
    { id: 1, name: 'Item 1' },
    { id: 2, name: 'Item 2' },
    { id: 3, name: 'Item 3' },
  ]);

  return (
    <List items={items}>
      {(item, index) => (
        <div style={{ padding: '10px' }}>
          {index}: {item.name}
        </div>
      )}
    </List>
  );
}

Virtual List (For Large Datasets)

tsx
import { List } from 'flexium/primitives';
import { state } from 'flexium/core';

function App() {
  const items = state(
    Array.from({ length: 10000 }, (_, i) => ({
      id: i,
      name: `Item ${i}`
    }))
  );

  return (
    <List
      items={items}
      virtual
      height={400}
      itemSize={50}
    >
      {(item, index) => (
        <div style={{ padding: '10px' }}>
          {index}: {item.name}
        </div>
      )}
    </List>
  );
}

With Custom Width

tsx
import { List } from 'flexium/primitives';
import { state } from 'flexium/core';

function App() {
  const items = state(
    Array.from({ length: 5000 }, (_, i) => `Item ${i}`)
  );

  return (
    <List
      items={items}
      virtual
      height={600}
      width={400}
      itemSize={60}
      overscan={5}
    >
      {(item, index) => (
        <div style={{
          padding: '15px',
          borderBottom: '1px solid #eee'
        }}>
          {item}
        </div>
      )}
    </List>
  );
}

With Stable Keys

tsx
import { List } from 'flexium/primitives';
import { state } from 'flexium/core';

function App() {
  const items = state([
    { id: 'a1', name: 'Alice' },
    { id: 'b2', name: 'Bob' },
    { id: 'c3', name: 'Charlie' },
    // ... thousands more
  ]);

  return (
    <List
      items={items}
      virtual
      height={500}
      itemSize={70}
      getKey={(item) => item.id}
    >
      {(item, index) => (
        <div style={{ padding: '20px' }}>
          <h3>{item.name}</h3>
          <p>ID: {item.id}</p>
        </div>
      )}
    </List>
  );
}

With Scroll Callbacks

tsx
import { List } from 'flexium/primitives';
import { state } from 'flexium/core';

function App() {
  const items = state(Array.from({ length: 10000 }, (_, i) => i));

  const handleScroll = (scrollTop: number) => {
    console.log('Current scroll position:', scrollTop);
  };

  const handleVisibleRangeChange = (start: number, end: number) => {
    console.log(`Visible items: ${start} to ${end}`);
  };

  return (
    <List
      items={items}
      virtual
      height={400}
      itemSize={50}
      onScroll={handleScroll}
      onVisibleRangeChange={handleVisibleRangeChange}
    >
      {(item, index) => <div>{item}</div>}
    </List>
  );
}

Props

PropTypeDefaultDescription
items() => T[]-Required. Reactive array or getter function returning the items to render.
children(item: T, index: number) => FNode-Required. Render function for each item. Receives the item and the index.
virtualbooleanfalseEnable virtualization for large lists.
heightnumber | string-Container height. Required when virtual is true.
itemSizenumber | SizeConfig-Height of each item. Required when virtual is true.
widthnumber | string'100%'Container width.
overscannumber3Extra items to render above/below viewport (only for virtual mode).
getKey(item: T, index: number) => string | number-Key extractor for stable identity.
onScroll(scrollTop: number) => void-Scroll callback (only for virtual mode).
onVisibleRangeChange(startIndex: number, endIndex: number) => void-Visible range callback (only for virtual mode).
classstring-CSS class name for the container.
styleRecord<string, string | number>-Inline styles for the container.

Item Size Configuration

The itemSize prop accepts either a number or a configuration object:

Fixed Size (Number)

tsx
itemSize={50}  // All items are exactly 50px tall

Fixed Size (Config)

tsx
itemSize={{
  mode: 'fixed',
  itemHeight: 50
}}

Variable Size (Estimated)

tsx
itemSize={{
  mode: 'variable',
  estimatedItemHeight: 80,
  getItemHeight: (index, item) => {
    // Optional: return actual height if known
    return item.isLarge ? 120 : 60;
  }
}}

Note: Variable height support is currently experimental. For best results, use fixed heights.

Render Function

The children prop is a render function that receives two parameters:

tsx
(item: T, index: number) => FNode
  • item: The data item to render
  • index: The current index of the item

Note: In virtual lists, the index matches the item's position in the original array.

tsx
// Correct usage
{(item, index) => (
  <div>{index}: {item.name}</div>
)}

Performance Tips

1. Use Stable Keys

Provide a getKey function that returns stable, unique identifiers for your items:

tsx
getKey={(item) => item.id}

2. Optimize Overscan

The overscan prop controls how many extra items are rendered beyond the visible viewport:

  • Lower values (1-2): Less memory usage, but may show blank areas during fast scrolling
  • Higher values (5-10): Smoother scrolling, but more items rendered

Default is 3, which works well for most cases.

3. Fixed Heights When Possible

Fixed heights provide the best performance:

tsx
itemSize={50}  // Preferred for uniform items

4. Sync Updates

When updating the items array, sync changes in a single signal update:

tsx
// Good - single update
items.set(newItems);

// Less efficient - multiple updates
newItems.forEach(item => items.update(arr => [...arr, item]));

Accessibility

List automatically includes accessibility attributes:

  • Container has role="list" and tabindex="0" (when virtual)
  • Each item has role="listitem" and appropriate aria-rowindex (when virtual)
  • Container includes aria-rowcount with total item count (when virtual)

Released under the MIT License.