InputGroup
A text input with things welded to its edges — icons, text, a spinner, a control of your own.
import { InputGroup } from "@glint/components/input-group.slint";import { Tokens } from "@glint/theme/tokens.slint";import { IconSet } from "@lucide";
export component Demo inherits Window { width: 560px; height: 300px; background: Tokens.color-background;
in-out property <string> search-query: "";
VerticalLayout { alignment: center; spacing: 16px; padding: 24px;
HorizontalLayout { alignment: center;
VerticalLayout { width: 360px;
InputGroup { leading-icon: IconSet.Search; placeholder: "Search documentation…"; trailing-text: "⌘K"; text <=> root.search-query; } } }
Text { text: root.search-query != "" ? "Searching: " + root.search-query : "Ready to search"; color: Tokens.color-muted-foreground; font-size: Tokens.typography-body-sm-size; horizontal-alignment: center; } }}Usage
import { InputGroup } from "@glint/components/input-group.slint";import { IconSet } from "@lucide";
export component AppWindow inherits Window { in-out property <string> url: "";
VerticalLayout { alignment: center;
InputGroup { leading-text: "https://"; trailing-icon: IconSet.ExternalLink; placeholder: "example.com"; text <=> root.url; } }}InputGroup embeds prefix and suffix adornments (icons, static text labels, spinners, or slotted custom chips) directly inside the shared border and focus ring of a text entry.
Examples
Input group variants
The variant property switches between single-line text, password entry, and a growing multi-line textarea:
InputGroupVariant.text: Standard single-line entry.InputGroupVariant.password: Obscured password characters.InputGroupVariant.multi-line: Dynamic multi-line composer that grows with content.
import { InputGroup, InputGroupVariant } from "@glint/components/input-group.slint";import { Tokens } from "@glint/theme/tokens.slint";import { IconSet } from "@lucide";
export component Demo inherits Window { width: 560px; height: 380px; background: Tokens.color-background;
VerticalLayout { alignment: center; spacing: 16px; padding: 24px;
HorizontalLayout { alignment: center;
VerticalLayout { width: 380px; spacing: 12px;
InputGroup { variant: InputGroupVariant.text; leading-icon: IconSet.Mail; }
InputGroup { variant: InputGroupVariant.password; leading-icon: IconSet.Lock; placeholder: "Master key"; }
InputGroup { variant: InputGroupVariant.multi-line; leading-icon: IconSet.MessageSquare; placeholder: "Write a comment or prompt…"; } } } }}Addon alignments and slotted children
The @children slot accepts custom elements placed according to addon-align:
InputGroupAddonAlign.inline-start: Beside the text at the leading edge.InputGroupAddonAlign.inline-end: Beside the text at the trailing edge.InputGroupAddonAlign.block-start: Header band above the text area.InputGroupAddonAlign.block-end: Footer toolbar band below the text area.
import { InputGroup, InputGroupVariant, InputGroupAddonAlign } from "@glint/components/input-group.slint";import { Kbd } from "@glint/components/kbd.slint";import { Tokens } from "@glint/theme/tokens.slint";import { IconSet } from "@lucide";
export component Demo inherits Window { width: 560px; height: 420px; background: Tokens.color-background;
VerticalLayout { alignment: center; spacing: 14px; padding: 24px;
HorizontalLayout { alignment: center;
VerticalLayout { width: 380px; spacing: 12px;
InputGroup { addon-align: InputGroupAddonAlign.inline-end; placeholder: "Jump to file…"; leading-icon: IconSet.FileText;
Kbd { text: "Ctrl+P"; } }
InputGroup { addon-align: InputGroupAddonAlign.inline-start; placeholder: "Custom leading badge"; leading-text: "v2.0"; }
InputGroup { variant: InputGroupVariant.multi-line; addon-align: InputGroupAddonAlign.block-start; placeholder: "Header row above text";
Text { text: "Markdown Supported"; color: Tokens.color-muted-foreground; font-size: Tokens.typography-body-sm-size; } }
InputGroup { variant: InputGroupVariant.multi-line; addon-align: InputGroupAddonAlign.block-end; placeholder: "Footer toolbar below text";
Text { text: "Press Shift+Enter for newline"; color: Tokens.color-muted-foreground; font-size: Tokens.typography-body-sm-size; } } } } }}Asynchronous loading state
Set loading: true to display an animated spinner in the trailing slot, indicating background validation or asynchronous lookups.
import { InputGroup } from "@glint/components/input-group.slint";import { Tokens } from "@glint/theme/tokens.slint";import { IconSet } from "@lucide";
export component Demo inherits Window { width: 560px; height: 300px; background: Tokens.color-background;
VerticalLayout { alignment: center; padding: 24px;
HorizontalLayout { alignment: center;
VerticalLayout { width: 360px;
InputGroup { leading-icon: IconSet.User; text: "felipemachado"; loading: true; loading-label: "Checking username availability…"; } } } }}API Reference
Properties
| Property | Type | Default | Description |
|---|---|---|---|
text | in-out string | no default | Two-way bound to the field contents. |
placeholder | in string | no default | Hint shown when the field is empty. |
enabled | in bool | true | When false, the group dims and stops accepting keystrokes. |
read-only | in bool | false | When true, the user can focus but not edit. |
variant | in InputGroupVariant | InputGroupVariant.text | What the entry holds — see InputGroupVariant. A multi-line group grows with its text, up to the max-height the call site sets; past that the text scrolls and the caret is scrolled back into view, exactly as in Textarea. |
invalid | in bool | false | When true, paints the border in destructive state. |
loading | in bool | false | While true, a spinner turns in the trailing addon — the async check (a username lookup, an address validation) reported inside the field rather than beside it. |
loading-label | in string | @tr("Loading") | Name of that spinner. Overridable because the group owns the element and a consumer cannot reach it (same reason Button exposes it). |
leading-icon | in LucideIcon | no default | Addon in front of the text — normally an IconSet icon, a prefix, or both. |
leading-text | in string | no default | |
trailing-icon | in LucideIcon | no default | Addon behind the text — a unit suffix, a shortcut hint, or both. |
trailing-text | in string | no default | |
addon-align | in InputGroupAddonAlign | InputGroupAddonAlign.inline-end | Where @children sit inside the border. |
has-focus | out bool | no default | Whether the group currently holds keyboard focus — also whether the focus ring is drawn, which is what makes the ring assertable. |
scroll-offset | out length | no default | How far the text is scrolled under the group, as a non-positive offset. Zero until a multi-line group has stopped growing at its max-height. |
Callbacks
| Callback | Description |
|---|---|
edited(string) | Fired on every keystroke with the current text. |
accepted(string) | Fired on Enter with the current text. A multi-line group breaks the line instead, the way any multi-line field does. |
Enums
| Enum | Values |
|---|---|
InputGroupVariant | text, password, multi-line |
InputGroupAddonAlign | inline-start, inline-end, block-start, block-end |
Accessibility
- Unified text entry node. Publishes a single
accessible-role: text-inputnode with the current text and placeholder. - Contextual adornments. Addon text (like units or URL schemes) remains accessible as supplementary context.
invalidannounces “Invalid”;loadingannouncesloading-label. The loading label defaults to"Loading", remains overridable and falls back to"Busy"when explicitly empty. Slint 1.17 publishes neitheraccessible-invalidnor a busy state, so both ride the description channel ADR-0013 opened; when both are present, the loading label is announced before"Invalid".- Focus ring. The focus ring envelops all welded addons, giving clear visual indication of active focus.