Forms
Form
Validation and submission orchestration over a native `<form>`.
Import
import { Form } from '@the_viveksingh/vivek-ui'The browser validates; Form makes the report usable
required, minLength, type="email" and pattern all work exactly as on plain HTML. Form intercepts submit, swaps in readable messages per field per failure, focuses the first invalid control, and hands { errors, submitting } to your layout. Nothing invalid ever reaches onSubmit. No dependency, no context, no controller.
Submit empty to see native rules reported readably, with focus moved to the first failure. The confirm rule is one line of `validate`. The two-second submit shows `submitting` driving the button.
<Form
messages={{
email: { valueMissing: 'We need an email to reach you' },
}}
validate={(v) =>
v.password !== v.confirm ? { confirm: 'Passwords do not match' } : null
}
onSubmit={async (values) => api.signup(values)}
>
{({ errors, submitting }) => (
<>
<Field label="Email" error={errors.email}>
<Input name="email" type="email" required />
</Field>
<Field label="Password" error={errors.password}>
<Input name="password" type="password" minLength={8} required />
</Field>
<Field label="Confirm" error={errors.confirm}>
<Input name="confirm" type="password" required />
</Field>
<Button type="submit" loading={submitting}>Create account</Button>
</>
)}
</Form>
// Plain children work too - the render function is only for layouts
// that want the errors and the pending state.Props
Generated from the package's own type declarations, so this table cannot drift from the code.
| Prop | Type | Default | Description |
|---|---|---|---|
onSubmit | (values: FormValues, event: FormEvent<HTMLFormElement>) => void | Promise<void> | — | Called only when validation passes. `values` is the whole form as `new FormData(form)` sees it — which means it also works for controls this library did not render. Return a promise and `submitting` tracks it. |
validate | (values: FormValues) => FormErrors | null | — | Cross-field rules, run after the native ones. Return `{ fieldName: message }` for anything wrong, or `null` when the values pass. Native attributes (`required`, `minLength`, `type="email"`, `pattern`) already validate for free — this is only for what attributes cannot express: matching passwords, at-least-one-of, date ranges. |
messages | Record<string, Partial<Record<string, string>>> | — | Readable replacements for the browser's messages, per field, per ValidityState key: `{ email: { valueMissing: 'We need an email to reach you' } }`. Unlisted failures fall back to the browser's own wording, which is localised but often stiff. |
children required | ReactNode | ((state: FormState) => ReactNode) | — | Plain children, or a render function that receives the full FormState. |
Every remaining prop is spread onto the root element, so all standard HTML and ARIA attributes work. className and style are merged with the library's own, never replaced, and the ref forwards to the root DOM node.
Rendering
Form declares 'use client' because it needs state, effects or event handlers. Importing it into a Server Component creates a client boundary at this component — everything above it stays on the server.