Components
GTK4's collection widgets take a model plus factories. The components in @gtkx/components drop those model, factory, and headerFactory props and take data plus renderers instead.
@gtkx/components is a separate install:
npm install @gtkx/componentsFull prop lists are in the @gtkx/components reference, and the hooks that bridge GObject state into React ship with @gtkx/react, whose signatures are in the @gtkx/react reference.
List components
ListView
ListView<T, S> wraps Gtk.ListView. Pass items as { id, value } pairs plus a renderItem. Selection is controlled: nothing stays selected unless selectedIds is fed back from onSelectionChanged.
import { ListView } from "@gtkx/components";
import * as Gtk from "@gtkx/gi/gtk";
import { GtkLabel } from "@gtkx/jsx/gtk";
<ListView<Task>
items={tasks.map((task) => ({ id: task.id, value: task }))}
selectionMode={Gtk.SelectionMode.MULTIPLE}
selectedIds={selectedIds}
onSelectionChanged={setSelectedIds}
estimatedItemHeight={56}
renderItem={({ item }) => <GtkLabel halign={Gtk.Align.START}>{item.title}</GtkLabel>}
/>Nesting ListItem.children turns the same component into a tree, with expandedIds and onExpandedChange driving expansion.
To group rows under headers, pass sections in place of items: each ListSection carries its own data array of items, and renderHeader draws the header above each group. ColumnView and DropDown accept the same pair.
GridView
GridView<T> gives Gtk.GridView, the icon-grid counterpart, the same items, renderItem, controlled selection, and size estimates, and adds minColumns, maxColumns, singleClickActivate, and onActivate.
ColumnView
ColumnView<T, S> renders a multi-column table. Columns come from the columns prop, each one requiring id, title, and renderCell. Sorting is controlled: onSortChanged reports the header click, and the caller sorts items before passing them in.
import { ColumnView, type ColumnViewColumn } from "@gtkx/components";
import { GtkLabel } from "@gtkx/jsx/gtk";
const columns: ColumnViewColumn<Employee>[] = [
{ id: "name", title: "Name", isSortable: true, renderCell: ({ item }) => <GtkLabel>{item.name}</GtkLabel> },
];
<ColumnView
sortColumn={sortColumn}
sortOrder={sortOrder}
onSortChanged={handleSortChange}
items={sortedEmployees.map((emp) => ({ id: emp.id, value: emp }))}
columns={columns}
/>DropDown
DropDown<T, S> takes items, or sections plus renderHeader, with single controlled selection through selectedId and onSelectionChanged. renderItem is optional and draws both the button face and the popup rows, renderListItem overrides the popup rows on their own, and with neither given each value is shown as a label.
import { DropDown } from "@gtkx/components";
<DropDown
items={SOURCE_TYPES.map((type) => ({ id: type, value: type }))}
selectedId={sourceType}
onSelectionChanged={(id) => setSourceType(id)}
/>ComboRow<T, S> from @gtkx/components/adw takes the same collection props and renders an Adw.ComboRow, presenting the choice as a row inside a preferences group, as the tutorial's preferences chapter does.
Next
Continue with Modals and Portals for the mounting model behind these components: createPortal, the rootElement container, and extra windows. The worked dialog walkthrough lives in the tutorial's Menus, Accelerators, and Shortcuts chapter.