Combobox
An autocomplete input and popup picker supporting single selection, multi-tag picking with chips, and rich item rows.
import { Button } from "@glint/components/button.slint";import { Combobox, ComboboxItem } from "@glint/components/combobox.slint";import { Tokens } from "@glint/theme/tokens.slint";import { IconSet } from "@lucide";
export component Demo inherits Window { width: 560px; height: 360px; background: Tokens.color-background;
in-out property <int> selected-idx: 0;
VerticalLayout { alignment: center; spacing: 16px; padding: 24px;
HorizontalLayout { alignment: center;
combo := Combobox { selected-index <=> root.selected-idx; items: [ { value: "next", label: "Next.js", icon: IconSet.Globe, description: "React Fullstack Framework" }, { value: "astro", label: "Astro", icon: IconSet.Rocket, description: "Content-driven Web Framework" }, { value: "slint", label: "Slint", icon: IconSet.Layout, description: "Native GUI Toolkit" }, { value: "vite", label: "Vite", icon: IconSet.Zap, description: "Frontend Tooling" }, ];
Button { text: root.selected-idx >= 0 ? "Framework: " + combo.items[root.selected-idx].label : "Select framework…"; haspopup: true; clicked => { combo.show(); } } } }
Text { text: "Chosen index: " + root.selected-idx; color: Tokens.color-muted-foreground; font-size: Tokens.typography-body-sm-size; horizontal-alignment: center; } }}Usage
import { Button } from "@glint/components/button.slint";import { Combobox, ComboboxItem } from "@glint/components/combobox.slint";
export component AppWindow inherits Window { in-out property <int> active-index: -1;
VerticalLayout { alignment: center;
combo := Combobox { selected-index <=> root.active-index; items: [ { value: "rust", label: "Rust" }, { value: "ts", label: "TypeScript" }, { value: "slint", label: "Slint" }, ];
Button { text: "Select Language"; haspopup: true; clicked => { combo.show(); } } } }}Combobox wraps a custom trigger in its @children slot. Calling show() opens a popup containing a search input over a scrollable suggestions list.
Typing in the search input updates the two-way query property and dynamically drives typeahead navigation.
When multiple: true is set, Combobox operates as a multi-selection tag picker where items can be toggled without closing the dropdown.
Examples
Multi-selection tag picker with chips
Set multiple: true and bind selected (an array of booleans matching items length).
Use ComboboxChips within or beside the trigger to display removable badges for selected items.
import { Button } from "@glint/components/button.slint";import { Combobox, ComboboxChips } from "@glint/components/combobox.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 560px; height: 360px; background: Tokens.color-background;
in-out property <[bool]> tag-selected: [true, false]; in-out property <[string]> chosen-labels: ["Bug"];
function sync-chosen-labels() { if (root.tag-selected[0]) { root.chosen-labels = root.tag-selected[1] ? ["Bug", "Documentation"] : ["Bug"]; } else { root.chosen-labels = root.tag-selected[1] ? ["Documentation"] : []; } }
VerticalLayout { alignment: center; spacing: 16px; padding: 24px;
HorizontalLayout { alignment: center; spacing: 8px;
combo := Combobox { multiple: true; selected <=> root.tag-selected; items: [ { value: "bug", label: "Bug" }, { value: "docs", label: "Documentation" }, ]; changed(index) => { root.sync-chosen-labels(); }
Button { text: "Select Tags…"; haspopup: true; clicked => { combo.show(); } } } }
HorizontalLayout { alignment: center;
ComboboxChips { labels: root.chosen-labels; removed(idx) => { if (root.chosen-labels[idx] == "Bug") { root.tag-selected[0] = false; } else { root.tag-selected[1] = false; } root.sync-chosen-labels(); } } } }}Rich item rows with icons, descriptions, and badges
ComboboxItem supports rich metadata including leading icons (icon) or avatars (avatar), secondary subtext (description), and trailing status badges (badge).
import { Button } from "@glint/components/button.slint";import { Combobox } from "@glint/components/combobox.slint";import { Tokens } from "@glint/theme/tokens.slint";import { IconSet } from "@lucide";
export component Demo inherits Window { width: 560px; height: 360px; background: Tokens.color-background;
VerticalLayout { alignment: center; padding: 24px;
HorizontalLayout { alignment: center;
combo := Combobox { content-width: 360px; items: [ { value: "v1", label: "Version 1.0", icon: IconSet.Tag, description: "Legacy release", badge: "Deprecated" }, { value: "v2", label: "Version 2.0", icon: IconSet.Tag, description: "Stable LTS release", badge: "Stable" }, { value: "v3", label: "Version 3.0", icon: IconSet.Sparkles, description: "Next generation build", badge: "Beta" }, ];
Button { text: "Select Release Version"; haspopup: true; clicked => { combo.show(); } } } } }}Grouped suggestions
Following the grouped rows model, use separator-before and heading-before on items to organize large option sets into titled categories without shifting item indices.
import { Button } from "@glint/components/button.slint";import { Combobox } from "@glint/components/combobox.slint";import { Tokens } from "@glint/theme/tokens.slint";import { IconSet } from "@lucide";
export component Demo inherits Window { width: 560px; height: 360px; background: Tokens.color-background;
VerticalLayout { alignment: center; padding: 24px;
HorizontalLayout { alignment: center;
combo := Combobox { items: [ { value: "chrome", label: "Google Chrome", icon: IconSet.Globe, heading-before: "Web Browsers" }, { value: "firefox", label: "Mozilla Firefox", icon: IconSet.Globe }, { value: "safari", label: "Apple Safari", icon: IconSet.Globe }, { value: "vscode", label: "Visual Studio Code", icon: IconSet.Code, separator-before: true, heading-before: "Development Tools" }, { value: "terminal", label: "Terminal", icon: IconSet.Terminal }, ];
Button { text: "Select Application"; haspopup: true; clicked => { combo.show(); } } } } }}API Reference
Properties
| Property | Type | Default | Description |
|---|---|---|---|
items | in [ComboboxItem] | no default | Options shown in the popup; value is yours to use, Glint only reads label. |
selected-index | in-out int | -1 | Two-way; index of the chosen item in items, or -1 for no selection. Single-selection mode only — see multiple. |
multiple | in bool | false | Several rows chosen at once rather than one: the tag picker. Rows announce themselves checkable, choosing one leaves the popup open so the next can be chosen without reopening, and the chosen values are drawn by a ComboboxChips the consumer puts inside its own trigger. |
selected | in-out [bool] | [] | Two-way; which rows are chosen in multiple mode, one flag per row of items in the same order. Size it to items: Slint cannot grow an array, so a flag past the end of this list is a choice with nowhere to land. A consumer who filters items by the query derives this beside it — which is also why the chips read their own list of labels rather than this one, and go on reading it while a query hides the rows. |
query | in-out string | no default | Two-way; current text in the search field. Reset to "" to clear the filter. |
highlighted-index | in-out int | 0 | Row highlighted by Enter; consumers rarely set this directly. |
placeholder | in string | @tr("Search…") | Hint shown in the search field when empty. |
empty-text | in string | @tr("No results found.") | Shown in place of the list when items is empty — the state a filtered popup lands in most often. Set it to "" to show nothing at all. |
clear-label | in string | @tr("Clear search") | Name of the control that empties the query, for assistive technology. |
content-width | in length | 320px | Pixel width of the popup panel. |
disabled | in bool | false | When true, the popup stays shut and the control announces itself unavailable. The trigger is @children and its own look belongs to it (ADR-0006) — bind the same flag there to dim it. |
is-open | out bool | no default | True while the popup is on screen. The trigger is @children, so rotating its chevron is yours to do — bind the rotation to this. |
Callbacks
| Callback | Description |
|---|---|
changed(int) | Fired with the chosen item's index when selected. |
Functions
| Function | Description |
|---|---|
show() | Opens the popup with the highlight back at the top. Mirrors PopupWindow.show() — wire it to a consumer-owned trigger so the popup opens from the keyboard as well as from a click. The disabled guard lives here rather than on each caller, because a consumer cannot suppress a popup from outside once it is up: every way in — the pointer, this function, both accessible actions — has to be refused at the same door. |
close() | Closes the popup. Mirrors PopupWindow.close(). |
ComboboxChips
A horizontal strip rendering selected values as individual tags with accessible remove buttons.
Properties
| Property | Type | Default | Description |
|---|---|---|---|
labels | in [string] | no default | What is chosen, in the order the chips read. |
remove-label | in string | @tr("Remove") | How the control that drops a chip is named. It is joined with the chip's own label, so each control says which value it drops rather than leaving a row of identical "Remove" buttons — Slint has no way to place the value inside an overridable sentence, so the join is a space. |
Callbacks
| Callback | Description |
|---|---|
removed(int) | Fired with the index into labels of the chip that was dropped. |
Accessibility
- Combobox semantics. The trigger node exposes the
comboboxrole, announcing expanded/collapsed states and current value. - Multi-select state. In
multiplemode, list items announce themselves as checkable with their checked boolean state. - Rich content announcement. Item descriptions and badges ride as
accessible-descriptionon the list item rather than creating noisy secondary nodes. - Chip controls. Each chip in
ComboboxChipsincludes a remove button with an accessible label explicitly naming the item to be removed (e.g.Remove Bug). - Keyboard navigation. ↑/↓ navigate items, Home/End jump to list ends, and typing in the search input runs query-driven prefix navigation.