FormControl is the wrapper you use to make form fields feel consistent.
Instead of reconverting label text, helper copy, required markers, and validation placement around every field by hand, FormControl gives those pieces one predictable vertical layout. This is helpful for text fields, selects, comboboxes, and any other control that belongs in a form.
Example
use fission::prelude::*;
let widget: Widget = FormControl {
id: None,
label: Some("Email address".into()),
required: true,
error: view.state().email_error.clone(),
helper: Some("We use this for receipts and login alerts.".into()),
child:
TextInput {
value: view.state().email.clone(),
label: Some("Email address".into()),
on_change: Some(email_change_action),
..Default::default()
}
.into(),
,
}
.into();
The wrapper controls layout and presentation. The child still owns the actual input event flow.
Field table
| | | |
|---|
| | Optional stable identity for the wrapper. | |
| | Visible label shown above the child. | |
| | The field or control being wrapped. | |
| | Validation message shown below the child. | If present, it takes precedence over helper. |
| | Supporting copy shown below the child when there is no error. | |
| | Whether the label should show a required marker. | |
What it does and what it does not do
FormControl solves visual consistency. It does not validate values, run reducers, or automatically connect label semantics to the child. That last point matters: the checked-in implementation renders the label as text above the child, but it does not inject that label into the child's accessibility semantics for you. If the child needs a semantic label, set it on the child widget too.
Specific advice
Use FormControl when the child is part of a larger form with validation or supporting copy. Skip it for controls that already come with their own integrated label and meaning, such as many simple Checkbox rows.
Production checklist
For FormControl, review the fields that change behavior before treating the widget as finished: id, label, child, error, helper, required. The goal is to make the product rule visible in state and actions, not hidden inside ad-hoc construction code.
If this widget appears inside an interactive flow, keep the surrounding action binding in the parent component and test that the flow still has one clear reducer path.
When child widgets are generated from data, give reordered or filtered rows an explicit WidgetId so retained local state and scroll behavior do not drift between items.
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 FormControl 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.
TextInput, Select, Combobox, and Checkbox.