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

Forms

Input

A single-line text input.

Rust
use herogpui::components::input::{Input, InputState};

Input on docs.rsSource at v0.14.0

Usage

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

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, InputState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![
        Input::new(self.demo_text("in-variant-primary", "", cx))
            .label(FieldVariant::Primary.label())
            .placeholder("Primary input")
            .variant(FieldVariant::Primary)
            .into_any_element(),
        Input::new(self.demo_text("in-variant-secondary", "", cx))
            .label(FieldVariant::Secondary.label())
            .placeholder("Secondary input")
            .variant(FieldVariant::Secondary)
            .into_any_element(),
    ])

Usage

Text adornments inherit the field's 14px text and 20px line height. Platform text replacement, composition updates, and paste use the same editable state as keyboard 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, InputState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![Input::new(self.demo_text("in-usage", "", cx))
        .label("Name")
        .placeholder("Enter your name")
        .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, InputState, 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.))
        .gap(px(16.))
        .child(
            Input::new(self.demo_text("in-surface", "", cx))
                .label("Name")
                .placeholder("Your name")
                .variant(FieldVariant::Secondary)
                .description("The lower-emphasis variant, for use on a surface"),
        )
        .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, InputState};
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![Input::new(self.demo_text("in-full", "", cx))
        .label("Name")
        .placeholder("Full width input")
        .full_width()
        .into_any_element()])

Input Types

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, InputState, InputType};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![
        Input::new(self.demo_text("in-pw", "", cx))
            .label("Password")
            .input_type(InputType::Password)
            .placeholder("Secret")
            .into_any_element(),
        Input::new(self.demo_text("in-num", "", cx))
            .label("Age")
            .input_type(InputType::Number)
            // `min`/`max` bound a numeric input, which is what
            // its validity is checked against.
            .min(18.)
            .max(120.)
            .placeholder("21")
            .into_any_element(),
        Input::new(self.demo_text("in-email", "", cx))
            .label("Email")
            .input_type(InputType::Email)
            .placeholder("user@example.com")
            .into_any_element(),
        Input::new(self.demo_text("in-url", "", cx))
            .label("Website")
            .input_type(InputType::Url)
            .placeholder("https://example.com")
            .into_any_element(),
        Input::new(self.demo_text("in-tel", "", cx))
            .label("Phone")
            .input_type(InputType::Tel)
            .placeholder("+1 (555) 000-0000")
            .into_any_element(),
    ])

Controlled

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, InputState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{App, px};
use herogpui::gpui;

let input_controlled = self
    .demo_text("in-controlled", "", cx)
    .read(cx)
    .value()
    .to_owned();

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        gpui::div()
            .w(px(256.))
            .flex()
            .flex_col()
            .child(Input::new(self.demo_text("in-controlled", "", cx))
                        .label("Name")
                        .placeholder("Enter your name")
                        .on_change(|_, _, _| {}))
            .into_any_element(),
        para(&format!("Value: {input_controlled}"), cx),
    ])

Also calls para (included when you copy).

States

Labels use 14px text with 20px lines; descriptions and errors use 12px text with 16px lines, independent of surrounding leading. 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, InputState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![
        Input::new(self.demo_text("in-required", "", cx))
            .label("Required")
            .placeholder("Enter a value")
            .is_required(true)
            .into_any_element(),
        Input::new(self.demo_text("in-invalid", "", cx))
            .label("Invalid")
            .placeholder("Taken name")
            .error_message("That name is taken.")
            .into_any_element(),
        Input::new(self.demo_text("in-disabled", "", cx))
            .label("Disabled")
            .placeholder("Unavailable")
            .is_disabled(true)
            .into_any_element(),
        Input::new(self.demo_text("in-clearable", "Ada", cx))
            .label("Clearable")
            .placeholder("Ada Lovelace")
            .is_clearable(true)
            .clear_hover_bg(cx.colors().accent.soft())
            .into_any_element(),
    ])

Focus Ring

`focus_ring(false)` keeps the input focusable and editable while suppressing only the visual focus ring. Invalid feedback and the field's normal chrome remain available. 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, InputState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![
        Input::new(self.demo_text("in-focus-ring-default", "", cx))
            .label("Default focus ring")
            .placeholder("Focus me")
            .into_any_element(),
        Input::new(self.demo_text("in-focus-ring-hidden", "", cx))
            .label("Focus ring hidden")
            .placeholder("Still editable")
            .focus_ring(false)
            .into_any_element(),
    ])

Compact Box

`height`, `padding_x` and `is_bare` are the box knobs: a 28px chromeless field with a leading icon, for a toolbar or a table cell the caller paints itself. Only the row height changes -- the text keeps its 14px size and 20px line. 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, InputState, TextField, 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![TextField::new(self.demo_text("in-compact", "", cx))
        .placeholder("Filter rows")
        .height(px(28.))
        .padding_x(px(8.))
        .is_bare(true)
        .start_content(gpui::svg()
                           .size(px(16.))
                           .path(icons::SEARCH)
                           .text_color(cx.colors().foreground)
                           .into_any_element())
        .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.

para
fn para(text: &str, cx: &App) -> gpui::AnyElement {
    let colors = cx.colors();
    gpui::div()
        .text_size(px(14.5))
        .line_height(px(24.))
        .text_color(colors.foreground)
        .max_w(px(720.))
        .child(text.to_owned())
        .into_any_element()
}

Anatomy

Rust types

Input

Input 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 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 styling
MethodValuesDescription
full_width()true | falseStretches to the available container width.
variant(FieldVariant)FieldVariant::Primary | FieldVariant::SecondaryPrimary field chrome or the lower-emphasis surface variant.
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: 20 entries ported, 5 partial, 2 not ported. Partial rows are marked in the tables; entries not ported are listed under each table with the reason.

Builders

Input builders
BuilderValuesDefaultDescription
input_type(InputType)PartialstringInputType::TextText, password, email, number, telephone, URL and search modes are represented; browser-only input types and native email/URL validation are not.
value(text)string—Seeds the owned InputState with the controlled value on the first render; InputState::set_value updates it afterwards.
default_value(text)string—Seeds the uncontrolled InputState once.
on_change(callback)Fn(event: change event) -> ()—Reports the current text after accepted edits; rejected maxLength/type edits do not emit duplicate changes.
placeholder(text)string—Placeholder shown while empty and unfocused.
is_disabled(bool)true | falsefalseRemoves focus and editing and applies disabled opacity.
is_read_only(bool)true | falsefalseKeeps focus and selection while suppressing edits.
is_required(bool)true | falsefalseParticipates in the HeroGPUI form validity registry.
name(text)string—Submission name stored with the current field value.
max_length(usize)number—Rejects edits that would exceed the character limit without reporting an unchanged value.
min_length(usize)number—Marks a non-empty value invalid below the character minimum.
pattern(predicate)Partialstring—A caller predicate replaces the browser regex-string attribute.
min(f64)Partialnumber | string—Numeric f64 lower bounds are supported; date/string native input bounds are not.
max(f64)Partialnumber | string—Numeric f64 upper bounds are supported; date/string native input bounds are not.
step(f64)Partialnumber | string—Numeric f64 step validity is supported; the native string forms are not.
full_width()true | falsefalseStretches to the available container width.
variant(FieldVariant)FieldVariant::Primary | FieldVariant::SecondaryFieldVariant::PrimaryPrimary field chrome or the lower-emphasis surface variant.
focus_ring(bool)true | falsetrueGPUI extension: hides only the visual focus ring while retaining focus, editing and invalid feedback.
Not ported2 HeroUI entries not ported
Input builders not ported
HeroUI v3Why it is not ported
Input.classNameTailwind class merging has no GPUI analogue.
Input.autoCompleteBrowser autofill hints have no desktop GPUI equivalent.

Parts

Input parts
PartDescription
InputSingle-line editable field with InputState-backed value, caret, selection and validation.

States

Input states
StateBuilderDescription
Hoverapply_field_chrome hoverChanges field background and border while unfocused.
Focusedtrack_focusDraws focused field background, border and ring. Registers platform text replacement, composition and paste against the same editable state.
Focus visiblefocused field treatmentPinned CSS adds no rule beyond the focused field treatment.
Invalidis_invalidUses invalid field ring and focused background.
Disabledis_disabled(bool)Applies theme disabled opacity and removes editing/focus.
Read onlyis_read_only(bool)Retains focus and selection while ignoring mutations.