Input
A single-line text input.
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.
Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
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(),
])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>.
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()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
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()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
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()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
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(),
])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
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).
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>.
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(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>.
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(),
])`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>.
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.
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 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
| Method | Values | Description |
|---|---|---|
full_width() | true | false | Stretches to the available container width. |
variant(FieldVariant) | FieldVariant::Primary | FieldVariant::Secondary | Primary field chrome or the lower-emphasis surface variant. |
// 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
| Builder | Values | Default | Description |
|---|---|---|---|
input_type(Partial | string | InputType:: | Text, password, email, number, telephone, URL and search modes are represented; browser-only input types and native email/URL validation are not. |
value( | string | — | Seeds the owned InputState with the controlled value on the first render; InputState::set_value updates it afterwards. |
default_value( | string | — | Seeds the uncontrolled InputState once. |
on_change( | Fn( | — | Reports the current text after accepted edits; rejected maxLength/type edits do not emit duplicate changes. |
placeholder( | string | — | Placeholder shown while empty and unfocused. |
is_disabled( | true | | false | Removes focus and editing and applies disabled opacity. |
is_read_only( | true | | false | Keeps focus and selection while suppressing edits. |
is_required( | true | | false | Participates in the HeroGPUI form validity registry. |
name( | string | — | Submission name stored with the current field value. |
max_length( | number | — | Rejects edits that would exceed the character limit without reporting an unchanged value. |
min_length( | number | — | Marks a non-empty value invalid below the character minimum. |
pattern(Partial | string | — | A caller predicate replaces the browser regex-string attribute. |
min(Partial | number | | — | Numeric f64 lower bounds are supported; date/string native input bounds are not. |
max(Partial | number | | — | Numeric f64 upper bounds are supported; date/string native input bounds are not. |
step(Partial | number | | — | Numeric f64 step validity is supported; the native string forms are not. |
full_width( | true | | false | Stretches to the available container width. |
variant( | FieldVariant:: | FieldVariant:: | Primary field chrome or the lower-emphasis surface variant. |
focus_ring( | true | | true | GPUI extension: hides only the visual focus ring while retaining focus, editing and invalid feedback. |
Not ported2 HeroUI entries not ported
| HeroUI v3 | Why it is not ported |
|---|---|
Input.className | Tailwind class merging has no GPUI analogue. |
Input.autoComplete | Browser autofill hints have no desktop GPUI equivalent. |
Parts
| Part | Description |
|---|---|
Input | Single-line editable field with InputState-backed value, caret, selection and validation. |
States
| State | Builder | Description |
|---|---|---|
Hover | apply_field_chrome hover | Changes field background and border while unfocused. |
Focused | track_focus | Draws focused field background, border and ring. Registers platform text replacement, composition and paste against the same editable state. |
Focus visible | focused field treatment | Pinned CSS adds no rule beyond the focused field treatment. |
Invalid | is_invalid | Uses invalid field ring and focused background. |
Disabled | is_disabled(bool) | Applies theme disabled opacity and removes editing/focus. |
Read only | is_read_only(bool) | Retains focus and selection while ignoring mutations. |