Components
@gtkx/components renders native collections from data and render functions. It manages their GTK models and factories. @gtkx/components/adw provides ComboRow and toast helpers.
@gtkx/components is a separate install:
npm install @gtkx/components@1.6.0See the component reference for props. Hooks that connect GObject state to React come from @gtkx/react.
List components
ListView
ListView<T, S> wraps Gtk.ListView. Pass items as { id, value } pairs plus a renderItem. Selection is controlled: update selectedIds from onSelectionChanged to retain the selection.
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> renders a grid with the same items, renderItem, controlled selection, and size estimates as ListView. Use minColumns and maxColumns to size the grid, and singleClickActivate and onActivate for activation.
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}
/>ColumnView also supports trees through ListItem.children, expandedIds, and onExpandedChange. The first visible column draws the expander and indentation; the other columns stay aligned at each depth. Hiding that column with visible: false moves the expander to the next visible column.
DropDown
DropDown<T, S> takes items, or sections plus renderHeader, with single controlled selection through selectedId and onSelectionChanged. Values have a default text display. Pass renderItem to draw both the selected value and the popup rows, or renderListItem to override only the popup rows.
import { DropDown } from "@gtkx/components";
<DropDown
items={SOURCE_TYPES.map((type) => ({ id: type, value: type }))}
selectedId={sourceType}
onSelectionChanged={setSourceType}
/>ComboRow<T, S> from @gtkx/components/adw takes the same collection props and renders an Adw.ComboRow inside a preferences group. See the tutorial's preferences chapter for an example.
GtkListBox
GtkListBox takes JSX rows directly, making it suitable for short lists such as a sidebar or settings group. Its selectedIndex prop controls the selected row:
import { AdwActionRow } from "@gtkx/jsx/adw";
import { GtkListBox } from "@gtkx/jsx/gtk";
<GtkListBox
cssClasses={["navigation-sidebar"]}
selectedIndex={views.findIndex((view) => view.id === activeId)}
onRowSelected={(row) => {
if (!row) return;
const view = views[row.getIndex()];
if (view) setActiveId(view.id);
}}
>
{views.map((view) => (
<AdwActionRow key={view.id} title={view.title} />
))}
</GtkListBox>Leave selectedIndex out for native selection, or set it to keep selection under application control. Use -1 or null to clear it. If the desired row has not mounted yet, GTKX keeps the current selection and applies the index once that row exists.
GTKX suppresses onRowSelected while applying its own selection updates. Other native selection changes still reach the handler, including changes made through native methods. Update the controlled value in that handler to keep the new selection; otherwise GTKX restores the requested row.
The tutorial's sidebar controls selection from the current route.
Toasts
Wrap the window's content in AdwToastOverlay, then share its ref through ToastProvider. Descendants can call useToast() to show a notification:
import { ToastProvider, useToast } from "@gtkx/components/adw";
import type * as Adw from "@gtkx/gi/adw";
import { AdwToastOverlay } from "@gtkx/jsx/adw";
import { GtkButton } from "@gtkx/jsx/gtk";
import { useRef } from "react";
const NotifyButton = () => {
const toast = useToast();
return (
<GtkButton
label="Show notification"
onClicked={() => toast.show({ title: "Task saved", useMarkup: false })}
/>
);
};
export const Notifications = () => {
const overlayRef = useRef<Adw.ToastOverlay | null>(null);
return (
<ToastProvider overlayRef={overlayRef}>
<AdwToastOverlay ref={overlayRef}>
<NotifyButton />
</AdwToastOverlay>
</ToastProvider>
);
};Render Notifications inside an application window. Clicking the button shows a toast over the content. Use useMarkup: false for titles that should display literally, including user text.
show() returns the native toast. Pass buttonLabel and onButtonClicked to add an action, and onDismissed when cleanup depends on its dismissal. Call useToast().dismiss(toast) to close the returned toast, or useToastOverlay().dismissAll() to clear the overlay. Both hooks come from @gtkx/components/adw.
useToast() requires a provider. Show toasts from events or effects after the overlay mounts; calling show() while its ref is empty creates a toast without displaying it. The Trash and Toasts tutorial adds an Undo action to a delete operation.
Next
Continue with Modals and Portals for the mounting model behind these components: createPortal, the rootElement container, and extra windows. The tutorial demonstrates dialogs in its Menus, Accelerators, and Shortcuts chapter.