Form

Framework adapters over @sometic/forms: React Form / hooks, Vue Form / composables, and the sometic-form custom element. Same FormController behavior everywhere, validation modes, field meta, submit, server errors, and field arrays.

PreviewForm layout example

Usage

tsx
import { useForm, Form } from "@sometic/react/form";

export function Example() {
    const form = useForm({ defaultValues: { email: "" } });
    return (
        <Form form={form} onSubmit={async (values) => console.log(values)}>
            <input name="email" />
            <button type="submit">Send</button>
        </Form>
    );
}
tsx
import { useForm, Form } from "@sometic/react/form";

export function Example(): JSX.Element {
    const form = useForm({ defaultValues: { email: "" } });
    return (
        <Form form={form} onSubmit={async (values) => console.log(values)}>
            <input name="email" />
            <button type="submit">Send</button>
        </Form>
    );
}
html
<script type="module">
    import { registerFormElements } from "@sometic/elements/form";
    registerFormElements();
</script>

<sometic-form>
    <input name="email" />
    <button type="submit">Send</button>
</sometic-form>

React/Vue Form props use onValid / optional onInvalid (see below). Wire submit through those handlers; the preview Usage snippet above is illustrative of composition shape.

For the engine itself (validators, drafts, a11y helpers), see Forms.

How it works

  1. Engine (@sometic/forms): createForm / FormController owns values, field registration, meta (dirty/touched/invalid), validation scheduling, submit (handleSubmit), server errors, field arrays, and optional feedback.
  2. Adapters: React useForm creates a controller; Form wraps a native <form noValidate>, provides context, and binds handleSubmit({ onValid, onInvalid }). Vue mirrors with composables and disposes on scope end. sometic-form auto-registers named controls and emits CE events.
  3. DOM: No separate “form resolve”; fields are ordinary inputs/selects/checkboxes bound via hooks or CE scan. Validation factories come from @sometic/validation.

Anatomy

PartRole
FormControllerSource of truth for values, meta, issues, submit
<form noValidate>Adapter host; native constraint validation disabled in favor of Sometic validators
FieldsRegistered paths (email, items[0].name) bound via hooks or CE auto-scan
FeedbackOptional validation / success / error messages from the controller

CE moves light children into an internal <form> and auto-registers named input / select / textarea.

Props / attributes

React FormProps

PropTypeDefaultDescription
formFormController<Record<string, unknown>>requiredController instance
onValidSubmitHandlers["onValid"], (values, { signal }) => void | Promise<void>requiredSuccess path
onInvalidSubmitHandlers["onInvalid"], (issues, values) => void | Promise<void>,Invalid submit
classNamestring,Host <form> class
childrenReactNode,Fields

Hooks (React)

APIRole
useForm(options)Creates FormController from CreateFormOptions
FormProviderContext provider
useFormContextRead controller from context
useFormState{ values, meta } snapshot subscription
useFormField{ value, meta, setValue, onBlur, form } for a path
useFieldArrayFieldArrayController for list fields

useForm / createForm options

NameTypeDefaultDescription
defaultValuesTValuesrequiredInitial values (cloned)
validatorsValidator[],Form-level validators
validationMode"onChange" | "onBlur" | "onSubmit" | "onTouched""onSubmit"Default field mode
debounceMsnumber0Default debounce for onChange
feedbackboolean | flagsall onValidation/success/error feedback

sometic-form attributes

AttributeDescription
novalidateObserved; form always uses Sometic validation
shadowOptional open shadow root

Access element.controller for the full FormController API.

Events / callbacks

SurfaceEventPayload
React/VueonValid / onInvalidvalues / issues
CEform-change{ values }
CEform-submit{ values }
CEform-invalid{ issues }
CEform-announce{ message }
Controllersubscribe(listener)void, any state change

Controlled vs uncontrolled

Form values are owned by the controller (always “controlled” at the engine layer). Individual inputs may be uncontrolled DOM nodes that you sync via setValue on change, or fully controlled through useFormField’s value. Default values seed once via defaultValues.

Form participation

Adapters render a real <form noValidate>. Named native controls still appear in FormData if you read the DOM, but Sometic submit uses controller values + validators. Prefer registering fields and reading onValid values rather than scraping the DOM. CE maps required attributes to built-in required validators.

Accessibility

  • Native labels and name attributes remain the primary association model.
  • Field meta exposes invalid state for aria-invalid.
  • Use @sometic/forms/a11y: announceFormErrors, focusFirstInvalid, formatIssueSummary.
  • CE invalid submit announces assertively and focuses the first invalid control.
  • Prefer noValidate + Sometic validators so AT and UI share one issue model.

Styling

Adapters do not ship visual chrome. Style the native <form> and fields with your design system. Map controller feedback via feedbackAttributes from @sometic/forms/feedback (data-feedback, role="status"|"alert").

Edge cases

  • React lifecycle: useForm does not auto-dispose; call form.dispose() if you need teardown.
  • Vue lifecycle: useForm disposes when the scope ends.
  • Async validation races, latest token wins; older runs abort.
  • Server errors, retained across client revalidation until clearServerErrors.
  • Double submit, use submit meta / disable the submit button while pending; pass signal from onValid into fetch.
  • SSR, create forms in effects or client-only entry; CE registration is browser-gated.

Performance notes

One controller per form instance; field subscriptions should be granular (useFormField / selectors) to avoid whole-tree rerenders. Debounce onChange validation with debounceMs. Import @sometic/react/form and @sometic/validation factories à la carte.

When to use / When not

Use for multi-field flows that need shared validation, dirty/touched meta, async submit, server error mapping, or field arrays, across React, Vue, or vanilla.

Do not use for a single uncontrolled input with native validation only, non-form app state (@sometic/store), or as a drop-in replacement for schema libraries without a SchemaAdapter.

FAQ

React vs Vue lifecycle? React useForm keeps the controller for the component lifetime and does not auto-dispose. Vue useForm disposes on scope dispose.

Why noValidate? So browser popups do not fight Sometic issues. Reimplement required/pattern with @sometic/validation factories.

How do server errors work? form.setServerErrors(issues), see Server errors. Prefer code: "server" when you want clearServerErrors(paths) to strip field issues.

Field arrays? useFieldArray / form.createFieldArray, see Field arrays.

Draft persistence? Compose createDraftController from @sometic/forms/drafts, see Persistence.

Is feedback on by default? Yes. Disable with feedback: false or selective flags.

Can I mix Sometic Input with Form? Yes, bind value/onValueChange (or checkbox/select equivalents) through useFormField.

FormData helper? The engine can produce FormData from values; see forms package docs for toFormData-style helpers in the API reference.