TextInput

TextInput is the main text-editing widget in Fission.
It covers ordinary single-line fields, multiline editors, password fields, syntax-highlighted editing, and input method editor composition. The important architectural rule is the same in every case: the widget does not own your product text. Your app state owns the text, the runtime delivers editing events, reducers update state, and the next component conversion pass renders the new value.

Example

use fission::prelude::*;
let widget: Widget = TextInput {
value: view.state().email.clone(),
label: Some("Email address".into()),
placeholder: Some("[email protected]".into()),
on_change: Some(ctx.bind(
    SetEmail(String::new()),
    reduce_with!((|state: &mut SettingsState, action: SetEmail, _| state.email = action.0)),
)),
on_submit: Some(save_email_action),
width: Some(320.0),
..Default::default()
}
.into();
The empty string inside SetEmail(String::new()) is only a placeholder so ctx.bind(...) knows the action type. When the user edits the field, the runtime replaces the payload with the current String before dispatching the action.

Core field table

Field
Type
Meaning
Notes / default behavior
id
Option<WidgetId>
Stable identity for focus, selection, and runtime edit state.
Defaults to None. Give important fields stable ids.
value
String
Current text value.
Controlled by app state.
label
Option<TextContent>
Semantic and visual label text.
Defaults to None. Use this for accessibility instead of relying only on placeholders.
placeholder
Option<TextContent>
Hint text shown when the field is empty.
Defaults to None.
helper_text
Option<TextContent>
Supporting text below the field.
Defaults to None.
error_text
Option<TextContent>
Validation message below the field.
Defaults to None.
counter_text
Option<TextContent>
Explicit counter text below the field.
Defaults to None.
on_change
Option<ActionEnvelope>
Action fired when the text changes.
The runtime serializes the current String into the payload.
on_submit
Option<ActionEnvelope>
Action fired when the user submits the field.
Uses the payload you provided on the envelope.
on_editing_complete
Option<ActionEnvelope>
Action fired when editing is explicitly completed.
Defaults to None.
on_tap_outside
Option<ActionEnvelope>
Action fired when the user taps or clicks away from the active field.
Defaults to None.
width
Option<f32>
Fixed width.
Defaults to None.
height
Option<f32>
Fixed height.
Defaults to None.
padding
Option<[f32; 4]>
Inner padding [left, right, top, bottom].
Defaults to themed field padding.

Editing behavior table

Field
Type
Meaning
Notes / default behavior
multiline
bool
Whether the field accepts newlines.
Defaults to false. Multiline fields scroll vertically.
autofocus
bool
Whether the field should request focus on mount.
Defaults to false.
enabled
bool
Whether the field is interactive.
Defaults to true. Disabled fields do not receive focus.
read_only
bool
Whether the field can be focused and selected but not edited.
Defaults to false.
min_lines
Option<usize>
Minimum visible line count for multiline fields.
Defaults to None.
max_lines
Option<usize>
Maximum visible line count for multiline fields.
Defaults to None.
obscure_text
bool
Whether the field masks characters.
Defaults to false. Useful for passwords.
obscuring_character
char
Character used when masking text.
Defaults to the implementation's bullet masking character.
prefix
Option<Widget>
Leading decoration inside the field.
Defaults to None.
suffix
Option<Widget>
Trailing decoration inside the field.
Defaults to None.
on_cursor_change
Option<ActionEnvelope>
Action fired when caret or selection anchor changes.
The runtime sends a CursorChanged payload.

Advanced editor and input table

Field
Type
Meaning
Notes / default behavior
keyboard_type
TextInputType
Preferred keyboard or input mode.
Defaults to plain text. Useful for email, number, and phone fields.
text_input_action
TextInputAction
Preferred return-key action.
Defaults to Done.
text_capitalization
TextCapitalization
Automatic capitalization hint.
Defaults to None.
max_length
Option<usize>
Maximum allowed text length.
Defaults to None.
input_formatters
Vec<InputFormatter>
Structured formatters applied during editing.
Defaults to an empty list.
borderless
bool
Remove the normal field chrome.
Defaults to false. Useful for embedded editors.
capture_tab
bool
Insert tab characters instead of moving focus.
Defaults to false.
auto_indent
bool
Copy leading whitespace on Enter.
Defaults to false. Useful for code-like editors.
styled_runs
Option<Vec<TextRun>>
Pre-styled text runs for syntax-highlighted rendering.
When present, the concatenated runs must match value exactly.
locale
Option<String>
Locale override for shaping and accessibility.
Defaults to None.
scroll_padding
Option<[f32; 4]>
Extra space kept around the caret during auto-scroll.
Defaults to None.

How text, actions, and input method editor state work together

TextInput is a controlled widget. The runtime handles low-level editing details such as caret movement, selection, clipboard operations, and input method editor composition, then dispatches higher-level events back into your reducer loop. Your reducer updates the text in GlobalState, and component conversion passes that value back in.
This is why component conversion stays pure even for rich text entry. The widget describes the field, the runtime manages the live editing mechanics, and state remains explicit.

Specific advice

Use a real label for accessibility; placeholder text is not a good substitute because it disappears once typing begins. Keep expensive parsing or validation out of the raw keystroke path unless you truly need it on every change. If a field needs stable focus or selection behavior across reconversions, give it a stable id.

Production checklist

For TextInput, review the fields that change behavior before treating the widget as finished: id, value, label, placeholder, helper_text, error_text. The goal is to make the product rule visible in state and actions, not hidden inside ad-hoc construction code.
Bind on_change to explicit reducer actions and test that the reducer handles unavailable, duplicate, or invalid input safely.
Set id only when identity must be stable across filtering, reordering, diagnostics, or tests; otherwise let Fission derive identity from structure.
Check the semantics tree for the user-facing label or role that makes this widget understandable without relying only on pixels.
Add at least one component or harness test that confirms the visible text, semantic role, action dispatch, and layout constraint that matter for this widget in context.
If a screen starts repeating the same TextInput setup, extract a named component around this widget. That keeps the reference API small while making product code easier to read and safer for generated code to copy.
FormControl, Editable, Combobox, and Environment, input, and input method editor.
Fission
A cross-platform, GPU-accelerated user interface framework for Rust. MIT licensed.
Copyright (c) 2026 Fission
Ready to use today. Widget APIs are expected to remain stable; some runtime and shell APIs may change before 1.0.0.
Fission 0.7.0