List

Die List stellt mehrere ListItems dar und bietet Sortierung, Filter und Suche.
import {
  ActionGroup,
  AlertBadge,
  Avatar,
  Button,
  ContextMenu,
  Heading,
  IconDomain,
  IconSubdomain,
  MenuItem,
  Text,
  typedList,
} from "@mittwald/flow-react-components";
import {
  type Domain,
  domains,
} from "@/content/04-components/structure/list/examples/domainApi";

export default () => {
  const DomainList = typedList<Domain>();

  return (
    <DomainList.List
      batchSize={4}
      aria-label="Domains"
      defaultViewMode="list"
      getItemId={(domain) => domain.id}
    >
      <DomainList.StaticData data={domains} />
      <ActionGroup>
        <Button color="accent">Anlegen</Button>
      </ActionGroup>
      <DomainList.Search />
      <DomainList.Filter
        property="type"
        mode="some"
        name="Typ"
      />
      <DomainList.Sorting
        property="hostname"
        name="Alphabetisch"
        direction="asc"
        directionName="aufsteigend"
        defaultEnabled
      />
      <DomainList.Sorting
        property="hostname"
        name="Alphabetisch"
        direction="desc"
        directionName="absteigend"
      />
      <DomainList.Table>
        <DomainList.TableHeader>
          <DomainList.TableColumn>
            Name
          </DomainList.TableColumn>
          <DomainList.TableColumn>
            Type
          </DomainList.TableColumn>
          <DomainList.TableColumn>
            TLD
          </DomainList.TableColumn>
          <DomainList.TableColumn>
            Hostname
          </DomainList.TableColumn>
        </DomainList.TableHeader>

        <DomainList.TableBody>
          <DomainList.TableRow>
            <DomainList.TableCell>
              {(domain) => domain.domain}
            </DomainList.TableCell>
            <DomainList.TableCell>
              {(domain) => domain.type}
            </DomainList.TableCell>
            <DomainList.TableCell>
              {(domain) => domain.tld}
            </DomainList.TableCell>
            <DomainList.TableCell>
              {(domain) => domain.hostname}
            </DomainList.TableCell>
          </DomainList.TableRow>
        </DomainList.TableBody>
      </DomainList.Table>
      <DomainList.Item
        textValue={(domain) => domain.domain}
        showTiles
      >
        {(domain) => (
          <DomainList.ItemView>
            <Avatar
              color={
                domain.type === "Domain" ? "blue" : "teal"
              }
            >
              {domain.type === "Domain" ? (
                <IconDomain />
              ) : (
                <IconSubdomain />
              )}
            </Avatar>
            <Heading>
              {domain.hostname}
              {!domain.verified && (
                <AlertBadge status="warning">
                  Unverifiziert
                </AlertBadge>
              )}
            </Heading>
            <Text>{domain.type}</Text>

            <ContextMenu>
              <MenuItem>Details anzeigen</MenuItem>
              <MenuItem>Löschen</MenuItem>
            </ContextMenu>
          </DomainList.ItemView>
        )}
      </DomainList.Item>
    </DomainList.List>
  );
}

Best Practices

  • Biete eine passende Ansicht an. Eine Rasteransicht eignet sich, wenn das Bild eines ListItems den Userflow bestimmt; bei mehreren Anwendungsfällen kann der User zwischen Ansichten wechseln.
  • Wähle eine Standardsortierung nach dem häufigsten Anwendungsfall. Ein Beispiel ist „Neueste zuerst“ bei einer Änderungshistorie; biete nur Sortieroptionen mit echtem Mehrwert.
  • Setze bei umfangreichen Lists Filter ein und gruppiere die Kategorien sinnvoll. Sinnvolle Gruppen sind zum Beispiel Typ, Größe oder Status.
  • Platziere weiteren Seiteninhalt oberhalb der List. So verursacht das Nachladen über den „Mehr anzeigen“-Button keine Layout-Verschiebungen; die List nimmt die volle Breite des Contents einer LayoutCard ein.
  • Beschreibe die List zugänglich. Ist sie der einzige Hauptinhalt einer Seite, erhält sie ein aria-label; hat sie eine eigene Heading, wird diese über aria-labelledby zugeordnet.

Ansichten

Die List unterstützt drei Ansichten: Liste, Raster und Tabelle. Die Default-Ansicht legst du über das Property defaultViewMode fest; sind mehrere Ansichten verfügbar, wechselt der User über den Ansichts-Button zwischen ihnen.

Listenansicht

Besonders geeignet, wenn viele Elemente übersichtlich, platzsparend und ansprechend dargestellt werden sollen. Nutze <List.Item />, um die List in der Listenansicht darzustellen.

Rasteransicht

Sinnvoll, wenn die Anzahl der Elemente überschaubar ist oder die visuelle Darstellung im Vordergrund steht – das ListItem sollte hier nur wenige Informationen enthalten. Für die Rasteransicht wird ebenfalls das <List.Item /> verwendet: Aktiviere sie über showTiles und deaktiviere die Listenansicht bei Bedarf mit showList={false}. Über maxTileWidth steuerst du die maximale Breite der Kacheln.

Tabellenansicht

Ideal für Daten, die schnell erfassbar sein müssen, während die optische Gestaltung zweitrangig ist. Nutze <List.Table />, um die List als Table darzustellen – dabei gelten die Guidelines der Table.

NameTypeTLDHostname

ListItems

Ein ListItem repräsentiert ein spezifisches Element einer Kategorie (z. B. eine Domain, E-Mail-Adresse oder ein Projekt) und zeigt nur die Informationen, die zum Verständnis nötig sind. In der Listenansicht besteht es typischerweise aus:

  • Avatar: Der Avatar steht am Anfang. Ein Icon spiegelt die Kategorie wider (z. B. ein Domain-Icon); ein hochgeladenes Image wird angezeigt, andernfalls erscheinen Initialen.
  • Titel und Untertitel: Der Titel gibt den Namen wieder, der Untertitel ergänzt weitere Informationen nach dem Muster „Beschreibung – 1. Information – 2. Information“.
  • Content Slots: Zusätzliche Bereiche (Top und Bottom Content), die flexibel befüllt werden können.
  • Aktionen: Interaktionen erfolgen über ein ContextMenu oder direkt als Buttons im ListItem.

In der Rasteransicht ist die Darstellung kompakter: Der Avatar wird größer und eckig, Top Content sowie die Accordion-Funktion entfallen.

Ein ListItem bietet das Property href, um das Element zu verlinken.

Mit Accordion

Das Accordion-Verhalten wird über die accordion-Property aktiviert. Dadurch lässt sich ein ListItem per Klick ein- oder ausklappen. Der erweiterte Inhalt wird in <Content slot="bottom" /> platziert.

Mit Checkboxen

Checkboxen in einem ListItem werden automatisch am Anfang der Zeile angeordnet. Ihre Funktionalität wird nicht von der List gesteuert und muss individuell implementiert werden. Achte darauf, dass die gesamte Zeile zur Auswahl genutzt werden kann – nutze dafür onAction der List.

Mit Content Slots

In einem ListItem kann zusätzlicher <Content /> (Top und Bottom Content) platziert werden. Die Position wird über das slot-Property gesteuert.

Mit ColumnLayout

Dem ListItem können die ColumnLayout-Properties s, m und l mitgegeben werden, um Seitenverhältnis und Umbruchverhalten von Header und Content zu steuern.

Da für die Spalten auch null gesetzt werden kann, lässt sich nicht zwingend benötigter Content in kleineren Ansichten ausblenden. In diesem Fall werden auch die entsprechenden Content Slots nicht angezeigt.

Mit ActionGroup

Verwende eine ActionGroup innerhalb des <Content />, um Buttons im ListItem zu platzieren.


Sortierung

Ist die Standardsortierung aktiv, zeigt der Sortierungs-Button nur „Sortierung“ an; wählt der User eine Option, wird der Button-Text entsprechend angepasst. Lege eine Sortiermethode über <List.Sorting /> an; mit customSortingFn und einem vorangestellten $ im property definierst du eine eigene Sortierung.

Benenne die Sortierung so, dass Kriterium und Reihenfolge sofort ersichtlich sind.

Do

Benenne die Sortierung so, dass eindeutig ersichtlich ist, wonach und in welcher Reihenfolge sortiert wird.

Don't

Verzichte auf Sortierformulierungen, die nicht eindeutig verständlich sind oder keine klare Reihenfolge vermitteln.

Sorting Properties

PropertyTypBeschreibung
customSortingFnSortingFn<T>Möglichkeit, eine eigene Sortierfunktion zu definieren
defaultEnabledboolean | "hidden"Bestimmt, ob die Sortierung als Default gesetzt wird; bei "hidden" ist die Option nicht sichtbar, wird aber im Hintergrund angewendet
direction"asc" | "desc"Auf- oder absteigende Sortierung
namestringDer Anzeigename der Sortier-Option
directionNamestringDer Anzeigename der Sortierrichtung
propertystringDas für die Sortierung verwendete Property

Filter

Ein Klick auf den Filter-Button öffnet ein ContextMenu, in dem Filter aktiviert oder deaktiviert werden können.

  • Standardmäßig erlaubt ein Filter die Mehrfachauswahl, damit User nach mehreren Kriterien filtern können; die Optionen erscheinen dann als Checkbox. Bei Einzelauswahl werden sie als RadioGroup dargestellt.
  • Jeder aktive Filter wird durch eine Badge visualisiert, die per Klick entfernt werden kann. Sind mindestens zwei Filter aktiv, erscheint zusätzlich ein „Filter zurücksetzen“-Button.
  • Mehrere Filter derselben Kategorie (z. B. Status, Art, Größe) werden in einem eigenen, passend benannten Filter-Button zusammengefasst. Filter ohne Kategorie gruppierst du unter einem allgemeinen „Filter“-Button.

Lege Filter über <List.Filter /> an. Über priority bestimmst du, ob ein Filter immer sichtbar ist (primary) oder erst im „Alle Filter“-Modal erscheint (secondary); „Alle Filter“ wird automatisch angezeigt, sobald es secondary Filter gibt. Die Anzeige des Filter-Werts lässt sich über eine Funktion anpassen (z. B. für Übersetzungen), und mit einem eigenen matcher plus vorangestelltem $ im property filterst du nach Werten, die nicht in der List vorkommen.

Aktive Filter-Badges sollten selbsterklärend sein. Bei mehrdeutigen Begriffen gib zusätzlichen Kontext an.

Domain
Unverifiziert

Do

Intuitiv verständliche Filter benötigen keinen zusätzlichen Kontext. Erklärungsbedürftige Filter sollten mit weiterem Text versehen werden.

Type Domain
Verifizierung Unverifiziert

Don't

Bei intuitiven Filtern sollte auf zusätzlichen Text verzichtet werden. Meist genügt ein einzelnes beschreibendes Wort.

Date Range Filter

Mit mode="dateRange" definierst du einen Filter, der die Auswahl eines Zeitraums ermöglicht. So lassen sich Einträge gezielt zwischen einem Start- und Enddatum eingrenzen.

RechnungDatum

Filter Properties

PropertyTypBeschreibung
defaultSelectedstring[]Array der als Default gesetzten Filter
matcherFilterMatcher<T, TProp, string>Definiert eine eigene Filterlogik für die Listenelemente
mode"all" | "some" | "one"Bestimmt, wie mehrere ausgewählte Filterwerte miteinander kombiniert werden
namestringDer Anzeigename des Filters
propertystringDas für die Filterung verwendete Property
valuesstring[]Die Optionen für den Filter

Suche

Verwende <List.Search /> innerhalb der List, um ein SearchField anzuzeigen. Standardmäßig startet die Suche automatisch; soll sie erst auf Enter auslösen, setze autoSubmit auf false. Öffnet sich die List in einem Modal, fokussiere das Suchfeld über autoFocus, damit der User direkt tippen kann.


Pagination

Standardmäßig zeigt die List maximal 10 ListItems; weitere lädt der User über den „Mehr anzeigen“-Button nach. Über batchSize änderst du die Anzahl, über hidePagination schaltest du die Pagination ab. Halte die Zahl niedrig: Über Suche, Filter und Sortierung findet der User gezielt, und viele gleichzeitig geladene Einträge kosten Performance.

Infinite Scroll

Für sehr lange Listen, in denen ohne konkretes Suchziel gestöbert wird, kann statt des „Mehr anzeigen“-Buttons Infinite Scroll aktiviert werden: Die nächste Seite lädt automatisch, sobald das Ende der Liste in den sichtbaren Bereich scrollt.

  • Infinite Scroll ist opt-in und sollte nicht der Default sein. Für kurze oder gezielt durchsuchte Listen ist der „Mehr anzeigen“-Button meist die bessere Wahl.
  • Der Mechanismus funktioniert unabhängig davon, ob die Daten statisch, asynchron oder über Hooks geladen werden, und respektiert manualPagination.
  • Während des Nachladens wird ein Ladeindikator am Ende der Liste angezeigt.

Lade- und Leeransichten

Loading View

Während die Daten initial geladen werden, zeigt die List eine Loading View aus Skeleton-Platzhaltern an. Ohne weitere Angabe wird ein generisches Skeleton verwendet. Über das loadingView-Property eines <List.Item /> – oder eines <TableCell /> in der Tabellenansicht – lässt sich diese Ansicht anpassen. Sie gilt in allen Ansichten (List, Tiles, Table) und auch für einzelne Items, die nach dem initialen Laden noch suspenden – etwa weil ihr Inhalt eigene Daten nachlädt. Gestalte sie mit Skeleton und SkeletonText so, dass sie dem Inhalt in Aufbau und Größe nahekommt, damit der Übergang ohne Layout-Sprung wirkt.

Empty View

Über emptyView zeigst du eine eigene Ansicht an, wenn die List keine Einträge enthält – in der Regel eine IllustratedMessage, die den User über einen Button einlädt, das erste Element zu erstellen. Liefert eine Suche oder ein Filter kein Ergebnis, zeigt emptySearchResultView einen entsprechenden Hinweis. Ist das jeweilige Property nicht gesetzt, verwendet die List eine vordefinierte Ansicht.

Initiale Suspense-Boundary

Beim initialen Laden umschließt die List das Laden der Daten standardmäßig mit einer eigenen Suspense-Boundary und zeigt währenddessen ihre Loading View. Über disableInitialSuspenseBoundary an der Datenquelle (<List.LoaderAsync />, <List.LoaderAsyncResource />, <List.LoaderHooks />) steuerst du dieses Verhalten:

WertVerhalten
false (Default)Die List rendert beim initialen Laden ihre eigene Loading View (Skeleton).
trueDie List rendert beim initialen Laden keine eigene Suspense-Boundary. Das Suspending wird an die nächste übergeordnete Boundary weitergereicht; die List erscheint erst mit geladenen Daten.

Belasse den Wert bei false, wenn die List den Hauptinhalt darstellt oder keine übergeordnete Ladeanzeige existiert. Setze ihn auf true, wenn die List in eine Seite eingebettet ist, die bereits einen eigenen Ladezustand anzeigt – so wird die List atomar dargestellt und der Layout-Shift zwischen Loading und Empty View vermieden. Zeigt deine Anwendung durchgängig eigene Ladezustände, kannst du true über den <ComponentDefaultsProvider /> als Standard festlegen.


Daten laden

Die List kann ihre Daten statisch oder asynchron laden.

Statische Daten

Für statische Daten wird <List.StaticData /> verwendet. Diese Variante benötigt keine zusätzliche Logik für Nachladen oder Filtern.

Asynchrone Daten

Mit <List.LoaderAsync /> werden Daten dynamisch aus einer API oder anderen asynchronen Quellen nachgeladen.

Die Loader-Funktion erhält ein options-Objekt und muss data sowie – für die Pagination – itemTotalCount zurückgeben.

PropertyTypBeschreibung
filtering{ [key: string]: { mode: "all" | "some" | "one"; values: any[] } }Enthält Filter für die Daten. Jedes Key-Value-Paar repräsentiert eine Filterbedingung für ein Datenfeld.
searchStringstringDer eingegebene Suchbegriff.
pagination{ offset: number; limit: number }Enthält Offset (Startpunkt) und Limit (maximale Anzahl an Datensätzen).
sorting{ [key: string]: "asc" | "desc" }Gibt an, nach welchen Datenfeldern sortiert werden soll.

Laden über Hooks

Mit <List.LoaderHooks /> werden Daten über React Hooks (z. B. TanStack Query oder SWR) nachgeladen. Der Einsatz von Suspense ist hierbei erforderlich.

Beim asynchronen Laden lassen sich Pagination (manualPagination), Sortierung (manualSorting), Filterung (manualFiltering) und Suche serverseitig verarbeiten.


Responsive Layout

Auf kleinen Bildschirmen passt sich der Header der List an: Ansicht und Sortierung werden in einem Icon-Button zusammengefasst, ebenso die Filter. Auf besonders kleinen Bildschirmen wandern diese Elemente zusammen mit der Suche eine Zeile nach unten.

Der Inhalt der ListItems bricht bei kleineren Bildschirmgrößen um. Top Content kann über ColumnLayouts auf kleinen Bildschirmen ausgeblendet werden – dabei dürfen nur Informationen entfallen, die der User nicht benötigt, um das ListItem zu verstehen.

Mobile Variante


Kombiniere mit ...

ActionGroup

Verwende <ActionGroup /> innerhalb der List, um eine ActionGroup mit Aktionen anzuzeigen, die sich direkt auf die Liste beziehen.

Summary

Verwende eine <ListSummary />, um eine Zusammenfassung anzuzeigen, beispielsweise die Gesamtsumme der Beträge. Über das position-Property legst du fest, ob die Summary oberhalb oder unterhalb der List erscheint.


Properties

PropertyTypeDefaultDescription
batchSizenumber-The number of items to be displayed on one page.
infiniteScrollbooleanfalseAutomatically loads the next batch of items when the user scrolls to the end of the list, instead of showing a "Show more" button.
hidePaginationbooleanfalseHides the pagination controls below the list.
emptySearchResultViewReactNode-The view rendered when a search or filter returns no results.
emptyViewReactNode-The view rendered when the list contains no items.
childrenReactNode-
wrapWithReactElement<unknown, string | JSXElementConstructor<any>>-A React element the component is wrapped with. The element is cloned and receives the component as its only child — useful to render the component inside a link, a tooltip trigger or any other wrapper without changing the surrounding markup.
refRef<HTMLSpanElement>-Allows getting a ref to the component instance. Once the component unmounts, React will set `ref.current` to `null` (or call the ref with `null` if you passed a callback ref). @see React Docs
keyKey-
disallowEmptySelectionboolean-Whether the collection allows empty selection.
disabledKeysIterable<Key>-The currently disabled keys in the collection (controlled).
selectionModeSelectionMode-The type of selection that is allowed in the collection.
selectedKeys"all" | Iterable<Key>-The currently selected keys in the collection (controlled).
defaultSelectedKeys"all" | Iterable<Key>-The initial selected keys in the collection (uncontrolled).
selectionBehaviorSelectionBehavior-Whether selecting an item replaces the current selection (`"replace"`) or adds to it (`"toggle"`).
accordionbooleanfalseMakes list items expandable. The expanded content is placed in `<Content slot="bottom" />`.
settingStorageKeystring-The key the lists settings (view mode, search, filters, sorting) are persisted under. Requires a `<SettingsProvider />` — without a key nothing is persisted.
loadingItemsCountnumber-The number of skeleton placeholder items rendered while data is loading. Defaults to the lists batch size.
getItemIdGetItemId<never>-Derives a stable ID from an items data. Used to deduplicate items across loaded batches and as the row ID in the table view.
defaultViewModeListViewMode"list"The view mode the list starts in. A persisted view mode takes precedence.
settingsStorageDefaultsListSettingsStorageDefaults-Defaults for how the lists settings are persisted.

Events

PropertyTypeDefaultDescription
onChangeOnListChanged<never, unknown>-Called with the list model whenever its state changes.
onSelectionChange((keys: Selection) => void)-Handler that is called when the selection changes.
onActionItemActionFn<never>-Called with the items data when the user activates a list item.

Accessibility

PropertyTypeDefaultDescription
aria-labelstring-An accessible label for the list.
aria-labelledbystring-The ID of the element labelling the list.

Auf dieser Seite