Skip to content
PorabuildHeroGPUI component library
v0.14.0 · Apache-2.0

Forms

Input Group

Combine an input with adjacent addons and controls.

Rust
use herogpui::components::input_group::InputGroup;

InputGroup on docs.rsSource at v0.14.0

Usage

Showing Usage, live: HeroGPUI compiled to WebAssembly. The canvas has no accessibility tree; each example's description and code below carry the same information.

Usage

Prefix and suffix text uses 14px text with 20px lines alongside the input. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{Input, InputAddon, InputGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .label("Website")
        .prefix(InputAddon::new("https://"))
        .input(
            Input::new(self.demo_text("ig-usage", "", cx))
                // v3's group example seeds the input with
                // `defaultValue`; `value` is the controlled
                // spelling of the same thing.
                .default_value("example.com")
                .placeholder("example.com"),
        )
        .into_any_element()])

Box Customisation

`padding_x` overrides the held field's exposed edges only -- a side with an addon keeps the addon's inset -- `height` resizes the single-line group, and `is_bare` drops the group's chrome. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{Input, InputAddon, InputGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .label("Compact")
        .height(px(28.))
        .padding_x(px(8.))
        .is_bare(true)
        .font_family(MONO_FONT)
        .prefix(InputAddon::new("$"))
        .input(
            Input::new(self.demo_text("ig-custom-box", "", cx))
                .placeholder("0.00"),
        )
        .into_any_element()])

Also calls MONO_FONT (included when you copy).

Hover Colour

`group_hover_bg` names the fill the hovered group box fades to, in place of `--field-hover`; a focused, invalid or disabled group keeps its own chrome. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{ActiveTheme, Input, InputAddon, InputGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .label("Website")
        .group_hover_bg(cx.colors().accent.soft())
        .prefix(InputAddon::new("https://"))
        .input(
            Input::new(self.demo_text("ig-hover-colour", "", cx))
                .placeholder("example.com"),
        )
        .into_any_element()])

Variants

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{FieldVariant, Input, InputAddon, InputGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![
        InputGroup::new()
            .label("Primary")
            .prefix(InputAddon::new("@"))
            .input(
                Input::new(self.demo_text("ig-v-primary", "", cx))
                    .placeholder("name@email.com"),
            )
            .into_any_element(),
        InputGroup::new()
            .label("Secondary")
            .variant(FieldVariant::Secondary)
            .prefix(InputAddon::new("@"))
            .input(
                Input::new(self.demo_text("ig-v-secondary", "", cx))
                    .placeholder("name@email.com"),
            )
            .into_any_element(),
    ])

In Surface

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{FieldVariant, Input, InputAddon, InputGroup, Surface};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![Surface::new()
        .padding(px(24.))
        .child(
            InputGroup::new()
                .label("Handle")
                .variant(FieldVariant::Secondary)
                .prefix(InputAddon::new("@"))
                .input(
                    Input::new(self.demo_text("ig-surface", "", cx))
                        .placeholder("name@email.com"),
                ),
        )
        .into_any_element()])

Loading State

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{Input, InputGroup, Spinner, SpinnerSize};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .label("Checking availability")
        .input(Input::new(self.demo_text("ig-loading", "example", cx)))
        .suffix(
            gpui::div()
                .pr(px(8.))
                .child(Spinner::new("input-group-spinner").size(SpinnerSize::Sm)),
        )
        .into_any_element()])

Required Field

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{Input, InputAddon, InputGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .label("Website")
        .is_required(true)
        .prefix(InputAddon::new("https://"))
        .input(
            Input::new(self.demo_text("ig-required", "", cx))
                .placeholder("name@email.com"),
        )
        .into_any_element()])

Disabled State

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{Input, InputAddon, InputGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .label("Website")
        .is_disabled(true)
        .prefix(InputAddon::new("https://"))
        .input(
            Input::new(self.demo_text("ig-disabled", "example.com", cx))
                .is_disabled(true),
        )
        .into_any_element()])

Full Width

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{Input, InputAddon, InputGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .label("Website")
        .full_width(true)
        .prefix(InputAddon::new("https://"))
        .input(
            Input::new(self.demo_text("ig-full", "", cx))
                .placeholder("name@email.com"),
        )
        .into_any_element()])

Text Prefix

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{Input, InputAddon, InputGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .prefix(InputAddon::new("https://"))
        .input(
            Input::new(self.demo_text("ig-text-prefix", "", cx))
                .placeholder("example.com"),
        )
        .into_any_element()])

Text Suffix

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{Input, InputAddon, InputGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .input(
            Input::new(self.demo_text("ig-text-suffix", "", cx))
                .placeholder("example"),
        )
        .suffix(InputAddon::new(".com"))
        .into_any_element()])

Icon Prefix and Text Suffix

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{ActiveTheme, Input, InputAddon, InputGroup, icons};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .prefix(gpui::div().pl(px(12.)).child(gpui::svg()
                                                  .size(px(16.))
                                                  .path(icons::MAIL)
                                                  .text_color(cx.colors().foreground)
                                                  .into_any_element()))
        .input(
            Input::new(self.demo_text("ig-icon-text", "", cx))
                .placeholder("name")
        )
        .suffix(InputAddon::new("@example.com"))
        .into_any_element()])

Copy Button Suffix

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{ActiveTheme, Button, Input, InputGroup, Size, Variant, icons};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .label("Website")
        .input(Input::new(self.demo_text("ig-copy", "example.com", cx)))
        .suffix(
            gpui::div().pr(px(4.)).child(
                Button::new("input-group-copy-btn")
                    .is_icon_only(true)
                    .variant(Variant::Ghost)
                    .size(Size::Sm)
                    .child(gpui::svg()
                               .size(px(16.))
                               .path(icons::COPY)
                               .text_color(cx.colors().foreground)
                               .into_any_element()),
            ),
        )
        .into_any_element()])

Icon Prefix and Copy Button

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{ActiveTheme, Button, Input, InputGroup, Size, Variant, icons};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .prefix(gpui::div().pl(px(12.)).child(gpui::svg()
                                                  .size(px(16.))
                                                  .path(icons::KEY)
                                                  .text_color(cx.colors().foreground)
                                                  .into_any_element()))
        .input(Input::new(self.demo_text(
            "ig-key",
            "sk_live_51H...",
            cx,
        )))
        .suffix(
            gpui::div().pr(px(4.)).child(
                Button::new("input-group-key-copy")
                    .is_icon_only(true)
                    .variant(Variant::Ghost)
                    .size(Size::Sm)
                    .child(gpui::svg()
                               .size(px(16.))
                               .path(icons::COPY)
                               .text_color(cx.colors().foreground)
                               .into_any_element()),
            ),
        )
        .into_any_element()])

Password Toggle

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{ActiveTheme, Button, Input, InputGroup, InputType, Size, Variant, icons};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

let ig_reveal = self.demo_flag("ig-reveal", false);

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .label("Password")
        .input(
            Input::new(self.demo_text("ig-pw", "correct horse", cx)).input_type(
                if ig_reveal {
                    InputType::Text
                } else {
                    InputType::Password
                },
            ),
        )
        .suffix(
            gpui::div().pr(px(4.)).child(
                Button::new("input-group-pw-toggle")
                    .is_icon_only(true)
                    .variant(Variant::Ghost)
                    .size(Size::Sm)
                    .child(gpui::svg()
                               .size(px(16.))
                               .path(if ig_reveal {
                                                   icons::EYE_OFF
                                               } else {
                                                   icons::EYE
                                               })
                               .text_color(cx.colors().foreground)
                               .into_any_element())
                    .on_press(cx.listener(move |this, _, _, cx| {
                        this.set_demo_flag("ig-reveal", !ig_reveal);
                        cx.notify();
                    })),
            ),
        )
        .into_any_element()])

Keyboard Shortcut

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{ActiveTheme, Input, InputGroup, Kbd, KbdVariant, icons};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .prefix(gpui::div().pl(px(12.)).child(gpui::svg()
                                                  .size(px(16.))
                                                  .path(icons::SEARCH)
                                                  .text_color(cx.colors().foreground)
                                                  .into_any_element()))
        .input(
            Input::new(self.demo_text("ig-kbd", "", cx)).placeholder("Search"),
        )
        .suffix(
            gpui::div()
                .pr(px(8.))
                .flex()
                .gap(px(4.))
                .child(Kbd::new().variant(KbdVariant::Light).child("Ctrl"))
                .child(Kbd::new().variant(KbdVariant::Light).child("K")),
        )
        .into_any_element()])

Badge Suffix

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{Chip, ChipLabel, ChipVariant, Color, Input, InputGroup, Size};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .label("Plan")
        .input(Input::new(self.demo_text("ig-badge", "Pro", cx)))
        .suffix(
            gpui::div().pr(px(8.)).child(
                Chip::new()
                    .size(Size::Sm)
                    .variant(ChipVariant::Soft)
                    .color(Color::Accent)
                    .child(ChipLabel::new().child("Trial")),
            ),
        )
        .into_any_element()])

Validation

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{Input, InputAddon, InputGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .label("Website")
        .is_required(true)
        .is_invalid(true)
        .error_message("Enter a valid URL")
        .prefix(InputAddon::new("https://"))
        .input(Input::new(self.demo_text("ig-invalid", "not a url", cx)))
        .into_any_element()])

With Prefix Icon

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{ActiveTheme, Input, InputGroup, icons};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .prefix(gpui::div().pl(px(12.)).child(gpui::svg()
                                                  .size(px(16.))
                                                  .path(icons::GLOBE)
                                                  .text_color(cx.colors().foreground)
                                                  .into_any_element()))
        .input(
            Input::new(self.demo_text("ig-prefix-icon", "", cx))
                .placeholder("name@email.com"),
        )
        .into_any_element()])

With Suffix Icon

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{ActiveTheme, Input, InputGroup, icons};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .input(
            Input::new(self.demo_text("ig-suffix-icon", "", cx))
                .placeholder("name@email.com"),
        )
        .suffix(gpui::div().pr(px(12.)).child(gpui::svg()
                                                  .size(px(16.))
                                                  .path(icons::CHECK)
                                                  .text_color(cx.colors().foreground)
                                                  .into_any_element()))
        .into_any_element()])

With Prefix and Suffix

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{ActiveTheme, Input, InputGroup, icons};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .prefix(gpui::div().pl(px(12.)).child(gpui::svg()
                                                  .size(px(16.))
                                                  .path(icons::SEARCH)
                                                  .text_color(cx.colors().foreground)
                                                  .into_any_element()))
        .input(
            Input::new(self.demo_text("ig-both", "", cx))
                .placeholder("Search..."),
        )
        .suffix(gpui::div().pr(px(12.)).child(gpui::svg()
                                                  .size(px(16.))
                                                  .path(icons::CLOSE)
                                                  .text_color(cx.colors().foreground)
                                                  .into_any_element()))
        .into_any_element()])

With TextArea

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{ActiveTheme, InputGroup, TextArea, icons};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .label("Note")
        .prefix(
            // The group's `:has(textarea)` rule owns the 8px
            // addon top padding; only the addon's horizontal
            // inset is spelled here, as on every bare-icon
            // addon slot.
            gpui::div().pl(px(12.)).child(gpui::svg()
                                              .size(px(16.))
                                              .path(icons::COPY)
                                              .text_color(cx.colors().foreground)
                                              .into_any_element()),
        )
        .text_area(
            TextArea::new(self.demo_text("ig-area", "", cx))
                .placeholder("Assign tasks or ask anything...")
                .rows(3),
        )
        .into_any_element()])

Usage Example

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{Input, InputAddon, InputGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .label("Amount")
        .description("Billed in US dollars.")
        .prefix(InputAddon::new("$"))
        .input(
            Input::new(self.demo_text("ig-example", "", cx)).placeholder("0.00"),
        )
        .suffix(InputAddon::new("USD"))
        .into_any_element()])

TextArea Usage Example

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{InputGroup, TextArea};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .label("Changelog")
        .description("Markdown is supported.")
        .text_area(
            TextArea::new(self.demo_text("ig-area-2", "", cx))
                .placeholder("Share a quick project update...")
                .rows(4),
        )
        .into_any_element()])

Addons

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{Input, InputAddon, InputGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .label("Amount")
        .description("Charged monthly.")
        .prefix(InputAddon::new("$"))
        .input(Input::new(self.group_amount.clone()).placeholder("0.00"))
        .suffix(InputAddon::new("USD"))
        .into_any_element()])

With a trailing action

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{Button, FieldVariant, Input, InputGroup, Size};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![InputGroup::new()
        .variant(FieldVariant::Secondary)
        .input(Input::new(self.input_email.clone()).placeholder("Email"))
        .suffix(
            gpui::div()
                .pl(px(8.))
                .pr(px(4.))
                .child(Button::new("input-group-send").label("Send").size(Size::Sm)),
        )
        .into_any_element()])

Helpers used by these examples

Plain functions over the public API, defined once here and copied with each example that calls them.

MONO_FONT
const MONO_FONT: &str = if cfg!(target_arch = "wasm32") {
    // The web shell bundles JetBrains Mono; the literal family "monospace"
    // resolves to nothing there and panics text resolution.
    "JetBrains Mono"
} else if cfg!(target_os = "macos") {
    "Menlo"
} else {
    "Consolas"
};

Anatomy

Rust types

InputGroup

Input Group is assembled from these builders. The API reference lists each one.

Customization

Shared appearance belongs on ThemeBuilder::components and .recipe("name"). Typed builders change how Input Group looks at one call site. Every builder also carries sx for placement, width and flex: it takes GPUI's own styling methods and refines them over the component's root after the theme, so an instance override wins. Behaviour builders live in the API reference below.

Styling

Input Group styling
MethodValuesDescription
full_width(bool)true | falseThe width lands on the outer wrapper as well as the group box: gpui resolves a percentage against the parent, and a content-sized parent stretches nothing.
variant(FieldVariant)FieldVariant::Primary | FieldVariant::SecondaryPrimary carries the field shadow; secondary drops it and paints the neutral default palette.
Rust
// The one slot for caller-owned low-level styling: GPUI's
// styling methods, refined over the root element last.
.sx(|el| {
    el.bg(gpui::rgba(0xffa500ff))             // background
        .text_color(gpui::rgba(0x000000ff))   // text
        .w(gpui::px(13.))                     // size
        .h(gpui::px(12.))
})

API reference

Against HeroUI v3.2.6: 32 entries ported, 4 partial, 11 not ported. Partial rows are marked in the tables; entries not ported are listed under each table with the reason.

Builders

Input Group builders
BuilderValuesDefaultDescription
input(Input) / text_area(TextArea) / child(AnyElement)PartialAnyElement | Fn(values: GroupRenderProps) -> AnyElement—Arbitrary children render after the field, and the typed slots compose the parts; the children-as-a-function form has no analogue, holds one field slot, and a disabled group dims these children without being able to make them pointer- or tab-inert.
full_width(bool)true | falsefalseThe width lands on the outer wrapper as well as the group box: gpui resolves a percentage against the parent, and a content-sized parent stretches nothing.
focus_ring(bool)true | falsetrueGPUI extension: hides only the group's visual focus ring while retaining the held field's focus and editing behavior.
id(ElementId)string—Optional GPUI identity. A named group reports role=group; an unnamed one produces no AccessKit node.
variant(FieldVariant)FieldVariant::Primary | FieldVariant::SecondaryFieldVariant::PrimaryPrimary carries the field shadow; secondary drops it and paints the neutral default palette.
is_disabled(bool) + Input::is_disabledtrue | falsefalseTakes this from the TextField around the group; folds it onto InputGroup and propagates it to the held field, so a group-disabled control tracks no focus and answers no keys, while one dim covers the whole box.
is_invalid(bool)true | falsefalseThe pinned TextField composition owns the flag; folds it on and drives the group's invalid chrome with it.
is_required(bool)true | falsefalseMarks the folded label as required.
label(SharedString)AnyElement—Composes Label as a sibling; folds it above the group box.
description(SharedString)AnyElement—Composes Description as a sibling; folds it below the group box.
error_message(SharedString)AnyElement—Shown only while the group is invalid, as the TextField does.
input(Input) + Input::variantPartial'primary' | 'secondary''primary'The group paints the shared chrome, so the held field's own variant flag is shadowed by the group's; the field accepts it through its builder.
input(Input) + Input::input_typestring'text'Password masking and numeric filtering ride on the held field.
input(Input) + InputState::valuestring—The controlled value is the bound InputState's text.
input(Input) + Input::default_valuestring—Seeds the held field's state once.
input(Input) + Input::placeholderstring—Placeholder of the held field.
input(Input) + Input::is_read_onlytrue | false—Read-only keeps the field focusable while edits are blocked.
text_area(TextArea) + TextArea::rowsnumber—Visible line count; three 20px lines plus 16px vertical padding per the field's own rows math.
text_area(TextArea) + Input::variantPartial'primary' | 'secondary''primary'The group paints the shared chrome, so the multi-line field's own variant flag is shadowed by the group's; the field accepts it through its builder.
text_area(TextArea) + InputState::valuestring—The controlled value is the bound InputState's text.
text_area(TextArea) + Input::default_valuestring—Seeds the held field's state once.
text_area(TextArea) + Input::placeholderstring—Placeholder of the held field.
text_area(TextArea) + Input::is_read_onlytrue | false—Read-only keeps the field focusable while edits are blocked.
prefix(AnyElement)AnyElement—Leading addon content: text, icons or anything else, drawn in placeholder colour.
suffix(AnyElement)AnyElement—Trailing addon content: text, buttons, badges or keyboards, keeping their own handlers.
Not ported11 HeroUI entries not ported
Input Group builders not ported
HeroUI v3Why it is not ported
InputGroup.classNameBrowser classes are unavailable.
InputGroup.styleBrowser inline styles are unavailable.
InputGroup.aria-labelNo accessibility tree to label.
InputGroup.aria-labelledbyNo accessibility tree to label.
InputGroup.aria-describedbyNo accessibility tree to describe.
InputGroup.aria-detailsNo accessibility tree to describe.
InputGroup.roleNo accessibility role mapping.
InputGroup.Input.classNameBrowser classes are unavailable.
InputGroup.TextArea.classNameBrowser classes are unavailable.
InputGroup.Prefix.classNameBrowser classes are unavailable.
InputGroup.Suffix.classNameBrowser classes are unavailable.

Parts

Input Group parts
PartDescription
InputGroupThe shared field chrome box: variant palette, focus-within ring, hover fill, invalid chrome, one disabled dim over the box, and the handleClick focus transfer to the held field.
InputGroupThe caller's Input rendered chrome-less: transparent, unrounded, flex_1 between the addons, with the padding dropped on whichever side an addon occupies.
InputGroupThe same shared chrome around a multi-line field; its presence switches the group to items-start with auto height and 8px addon top padding.
InputGroupLeading addon slot in placeholder colour with a 12px inset; a click on it is the group root's business, which focuses the held field.
InputGroupTrailing addon slot; a focusable suffix (a copy Button) keeps its own press, and the group's click-to-focus hands the focus back to the field afterwards.

States

Input Group states
StateBuilderDescription
Hoverfield.hover()Hovering paints bg-field-hover with --field-border-hover, and secondary swaps only the fill for --default-hover; the refinement is baked off while the focus is inside, and a disabled group paints none (status-disabled is hit testing: none first).
Focus Withinapply_field_chrome(focus_within)The held field's focus drives the group's status-focused-field ring, field-border-focus endpoint and pinned --field-focus fill.
Invalidapply_field_chrome(is_invalid)Status-invalid-field draws the danger outline and focused danger ring, with the --field-focus fill on both variants.
DisabledPartialis_disabledThe flag propagates to the held field, which stops tracking focus and answering keys, and one dim covers the whole group box — field, addon slots and any composed children; the folded label dims itself beside it. Only the held field is truly inert: GPUI cannot make arbitrary children pointer- or tab-inert, so a composed button in a disabled group still answers.