A native form with consolidated error handling, wrapping the Base UI Form. Wrap fields inside and validation, focus management, and server errors are handled in one place.

Playground

Every control maps to a prop. Try submitting empty, then with a typo'd email; flip on the server error to see the errors prop land on the right field.

States

The form has no state of its own; its fields carry it. Invalid, touched, and dirty arrive as the user works (focus rings the control being edited), and errors pushes a field into the invalid state from outside, before any submit:

That name is taken.

Styling states

The form element carries no state attributes; the fields inside do (data-invalid, data-valid, data-touched, data-dirty), so a form-wide treatment scopes through .ub-form:

.ub-form .ub-input[data-valid] {
  border-color: var(--ub-success);
}

Props

Everything a native form accepts passes through. Submission goes through onFormSubmit so you get parsed values instead of a raw event; it only fires once every field is valid.

PropTypeDefaultDescription
onFormSubmit(values) => voidโ€“Called with the field values (keyed by Field name) once they all pass validation.
validationMode"onSubmit" | "onBlur" | "onChange""onSubmit"When fields validate; after a submit attempt they re-validate on change.
errorsRecord<string, string | string[]>โ€“Server errors keyed by Field name; each FieldError renders its field's message.
actionsRefRefObject<FormActions>โ€“Imperative validate(), for all fields or one by name.

Agent instructions

Working with a coding agent? Paste this into its context, or point it at llms.txt for the whole library.

Form, from @usebones/react.
- Wrap Fields inside (FieldRoot name="..." + control + FieldError); submit with a Bones Button type="submit".
- onFormSubmit(values) fires only when all fields are valid; values are keyed by Field name.
- validationMode: "onSubmit" (default) | "onBlur" | "onChange".
- Server errors: pass errors={{ fieldName: "message" }} and render a bare <FieldError /> in that field; clear by passing undefined.
- Group related fields with FieldsetRoot + FieldsetLegend inside the form.
- Restyle in CSS via .ub-form (a 1rem column flex stack) scoping the fields' [data-invalid], [data-valid], [data-touched], [data-dirty]. Tokens only.