Do
Benenne die Sortierung so, dass eindeutig ersichtlich ist, wonach und in welcher Reihenfolge sortiert wird.
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="success">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> ); }
aria-label; hat sie eine eigene
Heading, wird diese über aria-labelledby
zugeordnet.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.
Besonders geeignet, wenn viele Elemente übersichtlich, platzsparend und
ansprechend dargestellt werden sollen. Nutze <List.Item />, um die List in der
Listenansicht darzustellen.
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.
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.
| Name | Type | TLD | Hostname |
|---|---|---|---|
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:
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.
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.
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.
In einem ListItem kann zusätzlicher <Content /> (Top und Bottom Content)
platziert werden. Die Position wird über das slot-Property gesteuert.
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.
Verwende eine ActionGroup innerhalb des <Content />, um
Buttons im ListItem zu platzieren.
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.
Benenne die Sortierung so, dass eindeutig ersichtlich ist, wonach und in welcher Reihenfolge sortiert wird.
Verzichte auf Sortierformulierungen, die nicht eindeutig verständlich sind oder keine klare Reihenfolge vermitteln.
| Property | Typ | Beschreibung |
|---|---|---|
customSortingFn | SortingFn<T> | Möglichkeit, eine eigene Sortierfunktion zu definieren |
defaultEnabled | boolean | "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 |
name | string | Der Anzeigename der Sortier-Option |
directionName | string | Der Anzeigename der Sortierrichtung |
property | string | Das für die Sortierung verwendete Property |
Ein Klick auf den Filter-Button öffnet ein ContextMenu, in dem Filter aktiviert oder deaktiviert werden können.
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.
Intuitiv verständliche Filter benötigen keinen zusätzlichen Kontext. Erklärungsbedürftige Filter sollten mit weiterem Text versehen werden.
Bei intuitiven Filtern sollte auf zusätzlichen Text verzichtet werden. Meist genügt ein einzelnes beschreibendes Wort.
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.
| Rechnung | Datum |
|---|---|
| Property | Typ | Beschreibung |
|---|---|---|
defaultSelected | string[] | Array der als Default gesetzten Filter |
matcher | FilterMatcher<T, TProp, string> | Definiert eine eigene Filterlogik für die Listenelemente |
mode | "all" | "some" | "one" | Bestimmt, wie mehrere ausgewählte Filterwerte miteinander kombiniert werden |
name | string | Der Anzeigename des Filters |
property | string | Das für die Filterung verwendete Property |
values | string[] | Die Optionen für den Filter |
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.
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.
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.
manualPagination.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.
Ü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.
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:
| Wert | Verhalten |
|---|---|
false (Default) | Die List rendert beim initialen Laden ihre eigene Loading View (Skeleton). |
true | Die 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.
Die List kann ihre Daten statisch oder asynchron laden.
Für statische Daten wird <List.StaticData /> verwendet. Diese Variante
benötigt keine zusätzliche Logik für Nachladen oder Filtern.
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.
| Property | Typ | Beschreibung |
|---|---|---|
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. |
searchString | string | Der 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. |
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.
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.
Verwende <ActionGroup /> innerhalb der List, um eine
ActionGroup mit Aktionen anzuzeigen, die
sich direkt auf die Liste beziehen.
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.
| Property | Type | Default | Description |
|---|---|---|---|
batchSize | number | - | The number of items to be displayed on one page. |
infiniteScroll | boolean | false | Automatically loads the next batch of items when the user scrolls to the end of the list, instead of showing a "Show more" button. |
hidePagination | boolean | false | Hides the pagination controls below the list. |
emptySearchResultView | ReactNode | - | The view rendered when a search or filter returns no results. |
emptyView | ReactNode | - | The view rendered when the list contains no items. |
children | ReactNode | - | |
wrapWith | ReactElement<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. |
ref | Ref<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 |
key | Key | - | |
disallowEmptySelection | boolean | - | Whether the collection allows empty selection. |
disabledKeys | Iterable<Key> | - | The currently disabled keys in the collection (controlled). |
selectionMode | SelectionMode | - | 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). |
selectionBehavior | SelectionBehavior | - | Whether selecting an item replaces the current selection (`"replace"`) or adds to it (`"toggle"`). |
accordion | boolean | false | Makes list items expandable. The expanded content is placed in `<Content slot="bottom" />`. |
settingStorageKey | string | - | The key the lists settings (view mode, search, filters, sorting) are persisted under. Requires a `<SettingsProvider />` — without a key nothing is persisted. |
loadingItemsCount | number | - | The number of skeleton placeholder items rendered while data is loading. Defaults to the lists batch size. |
getItemId | GetItemId<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. |
defaultViewMode | ListViewMode | "list" | The view mode the list starts in. A persisted view mode takes precedence. |
settingsStorageDefaults | ListSettingsStorageDefaults | - | Defaults for how the lists settings are persisted. |
| Property | Type | Default | Description |
|---|---|---|---|
onChange | OnListChanged<never, unknown> | - | Called with the list model whenever its state changes. |
onSelectionChange | ((keys: Selection) => void) | - | Handler that is called when the selection changes. |
onAction | ItemActionFn<never> | - | Called with the items data when the user activates a list item. |
| Property | Type | Default | Description |
|---|---|---|---|
aria-label | string | - | An accessible label for the list. |
aria-labelledby | string | - | The ID of the element labelling the list. |