Skip to content
componentsL2data-list

data-list

Shippedcomponenta11y AA · axe verified

A list that handles loading, empty and error for you and announces the result count to assistive tech. You write a single row component — everything else is provided.

Live example

<DataList state={…} />
4 users
AL
Ada Lovelace
Maintainer
GH
Grace Hopper
Admin
AT
Alan Turing
Member
RP
Radia Perlman
Member
aria-live announced: "Loaded 4 users."

Tutorial

Give it a state and a way to render one item. data-list composes state-boundary under the hood, so you inherit every accessibility guarantee.

  1. Install

    Pulls in data-list and its axe test. It depends on state-boundary, which the CLI adds for you if it is missing.

    terminal
    $ npx ibirdui add data-list
    + also adding dependency: state-boundary
    ✓ wrote components/data-list.tsx
  2. Write one row

    A plain component for a single item. No loading or error logic lives here — that is the list’s job.

    user-row.tsx
    function UserRow({ user }: { user: User }) {
      return (
        <li className="row">
          <Avatar src={user.avatar} />
          <span>{user.name}</span>
        </li>
      );
    }
  3. Render the list

    Pass the AsyncState, a label for screen readers, and a getKey. The render function turns each item into a row.

    users.tsx
    <DataList
      state={users}
      label="Team members"
      getKey={(u) => u.id}
    >
      {(u) => <UserRow user={u} />}
    </DataList>
  4. Result count is announced

    On success the list announces e.g. "4 team members loaded" via a polite live region — try the toggles in the example above.

    // status="success", data.length === 4
    // → announces: "4 team members loaded"

Props

The full surface of <DataList>.

PropTypeDescription
stateAsyncState<T[]>Required. The list state. An empty array resolves to the "empty" slot automatically.
children(item: T) => ReactNodeRequired. Renders one row. Called per item on success.
getKey(item: T) => stringRequired. Stable React key for each row.
labelstringAccessible name for the list, also used in the count announcement.
emptyReactNodeCustom empty slot. Defaults to a searching/empty message.
loadingRowsnumberHow many skeleton rows to show while loading. Default 4.

Accessibility

Guaranteed and verified by an axe-core test that fails the build on regression — not just documented.

Renders a real list — role="list" with role="listitem" rows — so structure is conveyed.
The result count is announced via aria-live after a successful load.
Loading state sets aria-busy and shows the configured number of skeleton rows.
The empty and error slots inherit state-boundary’s roles and focus behaviour.
Fully keyboard operable; interactive rows expose visible focus.
Shipped axe-core test exercises loading, empty, error and success.