Form
Form is a native <form> that knows about the Fields
inside it. It collects their values by name on submit, refuses to submit while
any of them is invalid and moves focus to the first one that failed, and routes
an errors object from your server back to the right message.
Four different controls, no onChange handlers, no form state library, no
useState per field. Every one of them reaches values under the name on its
Field.Root: Input as a string, Select as the chosen value, Checkbox and
Switch as booleans.
Import
import { Form } from "@forte-ui/react";Examples
Server errors
Constraint validation catches what the browser can see. Everything else —
uniqueness, permissions, anything that needs a round trip — comes back from the
server, and errors is how it gets to the right field. Keys match the name on
a Field.Root; values are a message or an array of them.
Base UI mirrors errors into its own state and drops a field's entry as soon as
that field changes. A server error describes a value the server has seen, so it
stops being true the moment the value moves — there is nothing to clear by hand.
Validation timing
validationMode on the form sets the default for every field in it;
validationMode on an individual Field.Root overrides it.
| Mode | Validates | Reach for it when |
|---|---|---|
onSubmit (default) | On submit, then on every change afterwards | Almost always. It does not interrupt someone mid-answer, and once they have seen an error it corrects live. |
onBlur | When a control loses focus | Long forms where a per-field verdict as you move down the page is genuinely useful. |
onChange | On every change | A value with an expensive or remote check — pair it with validationDebounceTime on the field. |
Accessibility
Base UI calls preventDefault() on the native submit event when onFormSubmit
is used, so onFormSubmit replaces onSubmit rather than sitting beside it.
The element is still a real <form>, so action, method and a native
non-JavaScript submit all work when you leave onFormSubmit off.
Each field's message is wired into its own control's aria-describedby — there
is no error summary at the top of the form. Add one yourself for long forms; SC
3.3.1 is satisfied either way, but a summary saves a lot of scrolling.
Theming
Form carries almost no styling — a <form> has no UA presentation worth
undoing. It lays out as a column because a form is nearly always a stack of
fields, and without it every consumer writes the same three declarations. Set
display through className for anything else.
| Property | Controls | Default |
|---|---|---|
--forte-form-gap | Space between the form's children | var(--forte-space-5) |
min-inline-size: 0 is set for the same reason it is on Field and
Fieldset: a text input inside refuses to shrink below its ~20-character
intrinsic width, so without it the form overflows a narrow column rather than
letting its fields wrap.
API reference
Form
| Prop | Type | Default | Description |
|---|---|---|---|
actionsRef | RefObject<FormActions | null> | A ref to imperative actions. `validate()` runs every field; pass a field name to run just one. | |
className | string | Additional class name(s). Applied after the internal styles so consumer utilities (e.g. Tailwind) win without needing `!important`. | |
errors | Errors | Errors that came from somewhere other than the browser — a server response, a form action, a schema parse. Keys are the `name` of a `Field.Root`; values are a message or an array of messages, which the matching `Field.Error` then renders. | |
onFormSubmit | ((formValues: FormValues, eventDetails: { reason: "none"; event: Event; }) => void) | Called on submit once every field passes validation, with the form's values collected by `name`. Base UI calls `preventDefault()` on the native event for you, so this replaces `onSubmit` rather than sitting beside it. | |
ref | Ref<HTMLFormElement> | Ref to the underlying `<form>` element. Declared explicitly rather than through `forwardRef` because the component is generic — a `forwardRef` wrapper would erase `FormValues` and with it the typing of `onFormSubmit`. | |
validationMode | FormValidationMode | onSubmit | When the fields inside are validated. `validationMode` on an individual `Field.Root` takes precedence over this. - `onSubmit` — validate on submit, then re-validate on change. The default, and the right one for most forms: it does not tell someone their email is invalid while they are still on the third character of it. - `onBlur` — validate a field when it loses focus. - `onChange` — validate on every keystroke. Pair it with `validationDebounceTime` on the field if `validate` is expensive. |
The generic is preserved, so <Form<SignUpValues> onFormSubmit={…}> types
values for you. Everything else is a native <form> prop and passes straight
through.