Native Inputs Do The Work
Every field part renders a real element: TextFieldInput, PasswordFieldInput, NumberFieldInput, PhoneFieldInput, DateFieldInput and TimeFieldInput are <input>, TextArea is a <textarea>, Radio is an <input type="radio">, and CheckboxInput, SwitchInput, SegmentedControlItemInput and SliderThumbInput are the inputs inside their components. PinField keeps the code in one visually hidden input behind its slots.
That gives forms the browser behavior for free:
name,value,required,pattern,min,max,minLengthandautoCompletego on the input part, exactly as on the native elementnew FormData(form)reads the values, and a submit withoutpreventDefault()posts them like any HTML formButtonhas no defaulttype, so inside a form it submits like a native<button>; passtype='button'for buttons that should not- the browser autofill, password managers and the
formattribute work on the inputs as usual
import { Button, Label, Text, TextField, TextFieldInput } from '@vega-ui/react';
import { FormEvent, useId, useState } from 'react';
export default function ProfileFormSubmit() {
const id = useId();
const [saved, setSaved] = useState('');
const onSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
setSaved(`${data.get('fullName')} <${data.get('email')}>`);
};What Each Field Submits
A native input submits the text it shows. For most fields that is the value itself; a few components keep a different value on the root:
| Component | Submitted with name on the input | Value on the root |
|---|---|---|
TextField, TextArea, PasswordField | the typed text | — |
NumberField | the formatted text, for example 2 500 | value / defaultValue as a number, NaN when empty |
DateField, DateRangeField, DateTimeField, TimeField | the typed text in format, for example 15.01.2026 | ISO value / defaultValue, see Dates and calendar systems |
Checkbox, Radio, Switch, SegmentedControl | value of each checked input | — |
Slider | each thumb value; name on the root applies to every thumb | value / defaultValue as an array |
Select | the selected value, through SelectHiddenSelect with a name | value / defaultValue |
When the server expects the root value rather than the shown text, mirror it into a hidden input from onChangeValue. The DateField example submits the ISO day this way. Listbox has no form part at all: render one hidden input per selected value, as in Listbox in a form.
import {
Button,
Label,
Select,
SelectCombobox,
SelectHiddenSelect,
SelectIcon,
SelectListbox,
SelectOption,
SelectValue,
Text,
} from '@vega-ui/react';Constraint Validation
required, pattern, type='email', min and max on the input parts block the submit and fill input.validity, the same as on native inputs. The browser shows its own message bubble unless you take over:
- listen to
onInvalidon the input and callevent.preventDefault()to hide the bubble, then readevent.currentTarget.validityto pick your message - or put
noValidateon the form and check the values yourself inonSubmit
The error look is opt-in and never derived from validity. Pass error to the field root (TextField, TextArea, NumberField, PinField) and to HelperText to switch them to the error colors. The library does not set ARIA for you: set aria-invalid on the input and point aria-describedby at the HelperText that carries the message.
import { Button, HelperText, Label, TextField, TextFieldInput } from '@vega-ui/react';
import { FormEvent, useId, useState } from 'react';
const getMessage = (input: HTMLInputElement) => {
if (input.validity.valueMissing) return 'Enter a postal code.';
if (input.validity.patternMismatch) return 'Use 5 digits, for example 10115.';
return '';
};
export default function NativeValidityMessage() {
const id = useId();
const [error, setError] = useState('');Controls without a native constraint, such as a Select without a choice, are validated in onSubmit: keep the error in state, set aria-invalid on SelectCombobox, and move focus to it. See Select required error.
Disabling A Group
Fieldset renders a native <fieldset>, so disabled on it disables every control inside by browser rules: the inputs stop taking focus and input, and disabled controls are left out of FormData. Put a reason next to the group with HelperText instead of disabling silently.
import { useState } from 'react';
import {
Checkbox,
CheckboxCheckedIcon,
CheckboxIndeterminateIcon,
CheckboxIndicator,
CheckboxInput,
Fieldset,
FieldsetLegend,
HelperText,
} from '@vega-ui/react';
Reset
A <button type='reset'> restores every uncontrolled field to its default:
- native parts return to
defaultValueordefaultChecked NumberField,PhoneField,PinField,DateField,DateRangeField,DateTimeField,TimeFieldandSliderrestore thedefaultValueof their root, also for a form linked with theformattribute- a controlled field keeps its
value, because the parent owns it: reset that state inonReseton the form
onReset is also the place to clear submit results and error messages kept in state.
Checklist
- Give every submitted input a
name; a field without one is not inFormData. - Keep validation rules on the input parts, so the browser, autofill and the server see the same constraints.
- Wire
aria-invalidandaria-describedbyyourself, and passerroronly as the visual state. - Use
Fieldsetwithdisabledfor a group, notdisabledon each control. - Check the same rules on the server: client validation is a convenience, not a guarantee.