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

Forms

Text Field

A composition-friendly field with label and validation.

Rust
use herogpui::components::input::TextField;

TextField 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

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::TextField;
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("tf-usage", "", cx))
        .label("Full name")
        .placeholder("Ada Lovelace")
        .description("As it appears on your ID.")
        .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, Surface, TextField};
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(
            TextField::new(self.demo_text("tf-surface", "", cx))
                .label("Full name")
                .placeholder("John")
                .variant(FieldVariant::Secondary)
                .description("Use the secondary variant on a surface"),
        )
        .into_any_element()])

With Description

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::TextField;
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("tf-desc", "", cx))
        .label("Full name")
        .placeholder("Enter username")
        .description("As it appears on your ID.")
        .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::TextField;
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("tf-req", "", cx))
        .label("Full name")
        .placeholder("John Doe")
        .is_required(true)
        .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::TextField;
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(
        "tf-dis",
        "Ada Lovelace",
        cx,
    ))
    .label("Full name")
    .placeholder("Auto-generated")
    .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::TextField;
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![TextField::new(self.demo_text("tf-full", "", cx))
        .label("Full name")
        .placeholder("John")
        .full_width()
        .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::TextField;
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("tf-validate", "", cx,))
            .label("Full name")
            .placeholder("jane_doe")
            .is_required(true)
            .validate(|value| value
                .trim()
                .is_empty()
                .then(|| "Name is required".into()))
            .into_any_element(),
        TextField::new(self.demo_text("tf-invalid", "", cx))
    .label("Full name")
    .placeholder("Ada Lovelace")
    // `isInvalid` marks it invalid from the outside, which is
    // what a server-side error looks like.
    .is_invalid(true)
    .error_message("Name is required")
    .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, TextField};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{App, px};
use herogpui::gpui;

let tf_controlled = self
    .demo_text("tf-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(TextField::new(self.demo_text("tf-controlled", "", cx))
                        .label("Full name")
                        .placeholder("Jane")
                        .on_change(|_, _, _| {}))
            .into_any_element(),
        para(&format!("Value: {tf_controlled}"), cx),
    ])

Also calls para (included when you copy).

Render Props

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

let tf_render_props = self.demo_text("tf-render-props", "Ada", cx);

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![{
        let field = tf_render_props.clone();
        TextField::new(tf_render_props)
            .content(move |state| {
                TextField::new(field.clone())
                    .label(format!(
                        "{} · {} · {}",
                        if state.is_required {
                            "required"
                        } else {
                            "optional"
                        },
                        if state.is_invalid { "invalid" } else { "valid" },
                        if state.is_focus_within {
                            "focused"
                        } else {
                            "unfocused"
                        },
                    ))
                    .placeholder("Enter your email")
                    .is_required(true)
                    .into_any_element()
            })
            .is_required(true)
            .into_any_element()
    }])

Error Message

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::TextField;
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("tf-error", "", cx))
        .label("Full name")
        .placeholder("Ada Lovelace")
        .is_required(true)
        .error_message("This field is required.")
        .into_any_element()])

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

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![TextArea::new(self.demo_text("tf-area", "", cx))
        .label("Bio")
        .placeholder("Write your message here...")
        .rows(4)
        .description("A `TextField` whose input is multi-line")
        .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::{InputType, TextField};
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("tf-pw", "", cx))
            .label("Password")
            .placeholder("••••••••")
            .input_type(InputType::Password)
            .into_any_element(),
        TextField::new(self.demo_text("tf-email", "", cx))
            .label("Email")
            .placeholder("user@example.com")
            .input_type(InputType::Email)
            .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

TextFieldInput

Text Field 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 Text Field 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

Text Field styling
MethodValuesDescription
full_width()true | falseStretches the root and inner control to the available width.
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: 19 entries ported, 7 partial, 4 not ported. Partial rows are marked in the tables; entries not ported are listed under each table with the reason.

Builders

Text Field builders
BuilderValuesDefaultDescription
content(render)AnyElement | Fn(values: TextFieldRenderProps) -> AnyElement—Replacement content receives disabled, invalid, read-only, required and complete focus state.
full_width()true | falsefalseStretches the root and inner control to the available width.
new(state)Partialstring—The caller-owned InputState entity supplies stable GPUI identity instead of a element id. Simultaneous fields need distinct InputState owners.
is_required(bool)true | falsefalseMarks the field required for native form validation.
is_invalid(bool)true | false—Forces invalid field chrome and error state.
validate(callback)PartialFn(value: string) -> ValidationError | true | null—A callback returns one optional validation message.
validation_behavior(ValidationBehavior)ValidationBehavior::Native | ValidationBehavior::AriaValidationBehavior::NativeSelects blocking native validation or non-blocking ARIA-style validation.
validation_errors(errors)string[]—Server messages take precedence over custom validation.
default_value(text)string—Seeds the uncontrolled InputState once.
on_change(callback)Fn(value: string) -> ()—Reports each accepted edit without duplicate reports for rejected edits.
is_disabled(bool)true | falsefalseRemoves focus and editing and applies disabled opacity.
is_read_only(bool)true | falsefalseKeeps focus and selection while suppressing edits.
name(text)string—Submission name stored with the current field value.
auto_focus(bool)true | falsefalseFocuses the inner input on its first render.
focus_ring(bool)true | falsetrueGPUI extension: hides only the inner input's visual focus ring while retaining focus and editing.
Not ported4 HeroUI entries not ported
Text Field builders not ported
HeroUI v3Why it is not ported
TextField.classNameBrowser styles are unavailable.
TextField.styleBrowser inline styles are unavailable.
TextField.renderRoot-element substitution has no GPUI equivalent.
TextField.aria-label / aria-labelledby / aria-describedby / aria-detailsGpui-pre 0.3.3 exposes no accessibility attribute tree.

Parts

Text Field parts
PartDescription
TextFieldRoot field state and vertical composition.
TextFieldPartialOptional label rendered before the editable control.
InputSingle-line InputState-backed editable control.
TextFieldPartialMulti-line field composition is supplied by the sibling TextArea component.
TextFieldPartialHelper message shown while the field is valid.
TextFieldPartialValidation or server message replacing the description while invalid.

States

Text Field states
StateBuilderDescription
Invalidresolved Input validityHides the description and shows the field error.
Disabledis_disabled(bool)Applies disabled opacity and removes focus and editing.
Focus withinTextFieldRenderState::is_focus_withinReplacement content receives focus-within while the inner input owns focus.
Focus visibleTextFieldRenderState::is_focus_visibleReplacement content receives keyboard-visible focus state.