Login form
Email and password with onChange validation. Submit is disabled while the form is invalid using form.Subscribe.
<FormInput
form={form}
name="email"
label="Email"
type="email"
validators={{
onChange: ({ value }) =>
!value.includes('@') ? 'Must be a valid email' : undefined,
}}
/>Contact form
Name, email, subject (FormSelect), and message (FormTextarea). Demonstrates mixing field types in a single form.
<FormSelect
form={form}
name="subject"
label="Subject"
options={[
{ value: 'general', label: 'General' },
{ value: 'support', label: 'Support' },
]}
/>Settings form
Mixed field types: FormInput for display name, FormTextarea for bio, FormSwitch for notifications, FormRadioGroup for theme, and FormCheckbox for opt-in.
<FormRadioGroup
form={form}
name="theme"
label="Theme"
options={[
{ value: 'light', label: 'Light' },
{ value: 'dark', label: 'Dark' },
{ value: 'system', label: 'System' },
]}
/>Validation showcase
Demonstrates three validator types on different fields:
- onChange — synchronous, fires on every keystroke (username min-length, age check).
- onBlur — fires when the field loses focus (email format).
- onChangeAsync — async with a simulated server check (username uniqueness).
validators={{
onChange: ({ value }) =>
value.length < 3 ? 'Min 3 characters' : undefined,
onChangeAsync: async ({ value }) => {
await new Promise((r) => setTimeout(r, 500));
return value === 'admin' ? 'Taken' : undefined;
},
}}Dynamic field array with FormFieldArray
Use the FormFieldArray utility for a cleaner API than raw form.Field with mode="array". It handles add/remove buttons and provides index, total, and remove in the render callback.
<FormFieldArray
form={form}
name="members"
label="Team members"
addLabel="Add member"
renderField={({ index, remove }) => (
<div className="flex gap-2">
<FormInput form={form} name={`members[${index}].name`} label="Name" />
<FormSelect
form={form}
name={`members[${index}].role`}
label="Role"
options={[
{ value: 'dev', label: 'Developer' },
{ value: 'design', label: 'Designer' },
]}
/>
<Button variant="outline" size="sm" onClick={remove}>Remove</Button>
</div>
)}
/>Linked fields with FormListenEffect
When one field changes, reset or update another. FormListenEffect renders nothing — it only fires side effects.
<FormSelect form={form} name="country" label="Country" options={countries} />
<FormSelect form={form} name="city" label="City" options={citiesForCountry} />
<FormListenEffect
form={form}
name="country"
listeners={{
onChange: ({ value }) => {
form.setFieldValue('city', '');
},
}}
/>Form with FormActions footer
FormActions provides a submit and optional reset button that auto-disables based on form state.
<form onSubmit={(e) => { e.preventDefault(); form.handleSubmit(); }}>
<FormInput form={form} name="name" label="Name" />
<FormInput form={form} name="email" label="Email" type="email" />
<FormActions form={form} submitLabel="Save changes" showReset />
</form>Live state preview with FormSubscribe
Use FormSubscribe to reactively display form values or derived state.
{
"name": "",
"email": ""
}<FormSubscribe
form={form}
selector={(state) => ({ values: state.values, dirty: state.isDirty })}
>
{({ values, dirty }) => (
<pre className="text-xs bg-muted p-3 rounded-lg">
{dirty ? '(modified) ' : ''}{JSON.stringify(values, null, 2)}
</pre>
)}
</FormSubscribe>Employee onboarding (complex multi-section)
A real-world multi-section form with fieldset/legend grouping, two-column grid layouts, and mixed field types. Demonstrates how to compose FormInput, FormSelect, FormRadioGroup, FormTextarea, FormCheckbox, and FormSwitch in a production-like structure.
<form className="w-full max-w-2xl space-y-8">
<fieldset className="space-y-4">
<legend className="text-sm font-semibold text-foreground mb-2">
Personal information
</legend>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<FormInput form={form} name="firstName" label="First name" />
<FormInput form={form} name="lastName" label="Last name" />
</div>
…
</fieldset>
<hr className="border-border" />
<fieldset className="space-y-4">
<legend className="text-sm font-semibold …">Employment details</legend>
…
</fieldset>
<FormActions form={form} submitLabel="Submit application" showReset />
</form>