Guides
Forms
Build an accessible form from Luke UI's Form and field components, and handle submit and reset.
A Luke UI form uses Form. Compose it from Luke UI fields and buttons.
Put fields in a layout primitive such as Stack. Form is not a layout
wrapper.
Visible labels
Pass a visible label to each field or radio group. Luke UI associates the label with its control
or group. A label takes textual content only. Place a link or button next to the field instead.
When a component supports a name without a visible label, pass aria-labelledby when content
already on the page supplies the name, or aria-label to supply the name directly. Check the
component's API for the names it accepts. Checkbox, switch, and radio options always take a visible
label. When your layout draws an option's label separately, compose the
Checkbox primitive or
Switch primitive, or the
Radio primitive.
A placeholder disappears after someone enters a value. Do not use a placeholder as the only label.
Descriptions
Pass description when someone needs information before they complete a field. Luke UI associates
the description with its control.
Keep each description to one sentence. Luke UI keeps it visible and associated with the control when an error message appears.
Required fields
Requiredness has three parts:
- Semantics. Set
isRequiredon each field that must be completed. With native validation, the browser blocks submission while a required field is empty. WhenvalidationBehavior="aria"is set, the control remains required, but the application or form library handles validation and submission. - Visible marker. Choose how the label shows that state with
necessityIndicator. - Accessible name. Required markers stay visual. Assistive technology reads required state from
the control. An optional marker with
'optional-label'becomes part of the accessible name.
Choose a marker with necessityIndicator:
'required-asterisk'adds a visual*after a required label.'required-label'adds visible "(required)" after a required label.'optional-label'adds "(optional)" after a definitely optional label.
Set necessityIndicator on Form to use the same convention across its
fields. An individual field can override that setting. When neither the field nor its Form specifies
an indicator, required labels display an asterisk.
A field with only aria-label or aria-labelledby shows no marker and gets no "(optional)" suffix.
When required fields use an asterisk, explain what the marker means near the start of the form. When
using 'optional-label', explain that fields are required unless marked optional.
'required-label' states requiredness in the label itself, so a separate legend is usually
unnecessary. The indicator changes the label, not the field's required state.
Related fields
Group related controls with a native <fieldset> and <legend> when they share one question or
topic, such as a set of notification options. The legend names the group.
Controlled and uncontrolled fields
Prefer an uncontrolled field when the application needs its value only during submission. Pass a
starting value through defaultValue or defaultSelected.
Use a controlled field when the application needs its value during each render. Pass value and
onChange, then store the value in state.
For a controlled CheckboxField or SwitchField, pass isSelected and onChange.
Submit
Prefer Form's action for ordinary submissions. Pass a React Action, often from useActionState,
so the handler receives FormData without a submit event. Use the pending flag for the submit
Button. After a successful Action, React resets uncontrolled fields.
Return server errors from the Action and pass them through validationErrors. Read
Server validation for that pattern.
Use onSubmit when the handler needs the submit event: calling preventDefault, reading the form
element, or wiring a form library. Libraries such as React Hook Form and TanStack Form expect
onSubmit. They own pending state and errors themselves. Read
React Hook Form and TanStack Form for those
integrations.
React's form Actions document
action and pending behaviour in detail.
Give the submit button type="submit".
Reset
Avoid a type="reset" button in ordinary forms. Someone can clear completed work by accident.
When a form needs to restore defaults, a reset control can be appropriate. The browser restores
uncontrolled fields to their starting values. Controlled fields keep the state React owns, so an
onReset handler must reset that state too.
That is separate from the automatic reset after a successful Action, which restores uncontrolled fields without a reset button.
Fields outside the form element
A field outside the form element can participate in native submission through the HTML form
attribute. That association does not establish React ancestry. If the field is outside Form's
React subtree, it does not inherit the shared necessity indicator, validation behaviour, or server
errors. Set necessityIndicator and validationBehavior on the field itself, and pass server
errors through its errorMessage.
Validation
Start with native form validation. Read Validation for browser constraints, custom rules, server errors, and focus after an unsuccessful submission. When a form library owns the form state, read React Hook Form or TanStack Form.