Skip to content

Component TextInput

MeowLynxSea edited this page Feb 14, 2026 · 10 revisions

TextInput

Single-line text input.

TextInput owns cursor/selection state and is optimized for “uncontrolled” usage.

When to use

  • Forms and settings where users type short values (names, ids, paths)
  • Search bars (or use the higher-level SearchInput)

Not a good fit:

  • Multi-line input (use TextArea)

Example

use gpui::ElementId;
use yororen_ui::component::text_input;

let input = text_input()
    .key(ElementId::from((ElementId::from("settings"), "username")))
    .placeholder("Username")
    .on_change(|value, _window, _cx| {
        // value: SharedString
        // ...
    });

Notes

  • TextInput owns internal cursor/selection state, so stable key(...) is recommended in lists.

Avoid common pitfalls

Prefer "uncontrolled" input (recommended)

In most cases, let TextInput own its editing state (cursor/selection) and use on_change to observe the latest value.

This avoids performance issues and keeps typing smooth.

use gpui::ElementId;
use yororen_ui::component::text_input;

let input = text_input()
    .key(ElementId::from(("profile", "username")))
    .placeholder("Username")
    .on_change(|value, _window, cx| {
        // Store value somewhere if needed (validation, enable/disable buttons, etc.).
        // Keep this handler lightweight.
        let _ = value;
        cx.notify();
    });

Be careful with .content(...) ("controlled" input)

TextInput supports setting its content via .content(...), but you should not feed .content(...) back into the same TextInput on every render (e.g. .content(state_value) where state_value is updated from on_change).

That pattern forces a sync loop (internal edit state -> external state -> re-sync back into the input), which can cause:

  • noticeable typing lag
  • cursor/selection jumps
  • extra layout work every keystroke

Use .content(...) only for explicit one-off sync points, such as:

  • initial value
  • programmatic reset ("Clear" button)
  • loading a saved profile into the input

When you need frequent updates, prefer the uncontrolled pattern above.

API

  • text_input(): constructor
  • id(...) / key(...): stable identity (recommended for keyed state management; auto-generated if not provided)
  • placeholder("...")
  • disabled(bool)
  • content(SharedString): set content (controlled sync; see notes)
  • max_length(usize): maximum number of characters allowed
  • on_change(|value, window, cx| ...)
  • on_submit(|value: SharedString, window: &mut gpui::Window, cx: &mut App| ...)
  • Styling: bg/border/focus_border/text_color/height

Defaults

  • Height: 36px
  • Placeholder: empty string

Notes

  • Requires one-time registration via yororen_ui::component::init(cx).

Clone this wiki locally