Form

A form component built on top of react-hook-form with validation and field arrays support.

Anatomy

<Form.Root>
  <Form.Field>
    <Form.Label />
    <Form.Control />
    <Form.Description />
    <Form.Message />
  </Form.Field>
  <Form.Submit />
</Form.Root>
<Form.Root>
  <Form.FieldArray.Root>
    <Form.FieldArray.Field>
      <Form.Label />
      <Form.Control />
      <Form.FieldArray.Controls />
    </Form.FieldArray.Field>
  </Form.FieldArray.Root>
</Form.Root>

Usage

import { Form } from '@ivaldi/ui'
const form = useForm({
  defaultValues: {
    username: '',
    email: '',
  }
})

return (
  <Form.Root {...form}>
    <form onSubmit={form.handleSubmit(console.log)} className="space-y-4">
      <Form.Field
        control={form.control}
        name="username"
        render={({ field }) => (
          <div>
            <Form.Label>Username</Form.Label>
            <Form.Control>
              <Input {...field} />
            </Form.Control>
          </div>
        )}
      />
      <Form.Field
        control={form.control}
        name="email"
        render={({ field }) => (
          <div>
            <Form.Label>Email</Form.Label>
            <Form.Control>
              <Input {...field} type="email" />
            </Form.Control>
          </div>
        )}
      />
      <Button type="submit">Submit</Button>
    </form>
  </Form.Root>
)

With Validation

const form = useForm({
  defaultValues: {
    username: '',
    email: '',
  },
  resolver: zodResolver(
    z.object({
      username: z.string().min(3),
      email: z.string().email(),
    })
  ),
})

return (
  <Form.Root {...form}>
    <form onSubmit={form.handleSubmit(console.log)} className="space-y-4">
      <Form.Field
        control={form.control}
        name="username"
        render={({ field }) => (
          <div>
            <Form.Label>Username</Form.Label>
            <Form.Control>
              <Input {...field} />
            </Form.Control>
            <Form.Message />
          </div>
        )}
      />
      <Form.Field
        control={form.control}
        name="email"
        render={({ field }) => (
          <div>
            <Form.Label>Email</Form.Label>
            <Form.Control>
              <Input {...field} type="email" />
            </Form.Control>
            <Form.Message />
          </div>
        )}
      />
      <Button type="submit">Submit</Button>
    </form>
  </Form.Root>
)

Field Array

const form = useForm({
  defaultValues: {
    items: [{ name: '' }]
  }
})

return (
  <Form.Root {...form}>
    <form onSubmit={form.handleSubmit(console.log)} className="space-y-4">
      <Form.FieldArray
        control={form.control}
        name="items"
        render={({ fields, append, remove }) => (
          <div className="space-y-4">
            {fields.map((field, index) => (
              <Form.FieldArrayField key={field.id}>
                <Form.Label>Item {index + 1}</Form.Label>
                <div className="flex gap-2">
                  <Form.Field
                    control={form.control}
                    name={`items.${index}.name`}
                    render={({ field }) => (
                      <Input {...field} />
                    )}
                  />
                  <Form.FieldArrayRemove
                    onClick={() => remove(index)}
                  />
                </div>
              </Form.FieldArrayField>
            ))}
            <Form.FieldArrayAdd
              onClick={() => append({ name: '' })}
            />
          </div>
        )}
      />
      <Button type="submit">Submit</Button>
    </form>
  </Form.Root>
)

With Description

<Form.Field
  control={form.control}
  name="username"
  render={({ field }) => (
    <div>
      <Form.Label>Username</Form.Label>
      <Form.Control>
        <Input {...field} />
      </Form.Control>
      <Form.Description>
        This is your public display name.
      </Form.Description>
      <Form.Message />
    </div>
  )}
/>

With Different Controls

<Form.Root {...form}>
  <form onSubmit={form.handleSubmit(console.log)} className="space-y-4">
    <Form.Field
      control={form.control}
      name="switch"
      render={({ field }) => (
        <div className="flex items-center justify-between">
          <Form.Label>Notifications</Form.Label>
          <Form.Control>
            <Switch
              checked={field.value}
              onCheckedChange={field.onChange}
            />
          </Form.Control>
        </div>
      )}
    />
    <Form.Field
      control={form.control}
      name="select"
      render={({ field }) => (
        <div>
          <Form.Label>Category</Form.Label>
          <Form.Control>
            <Select.Root
              value={field.value}
              onValueChange={field.onChange}
            >
              <Select.Trigger>
                <Select.Value placeholder="Select a category" />
              </Select.Trigger>
              <Select.Content>
                <Select.Item value="1">Category 1</Select.Item>
                <Select.Item value="2">Category 2</Select.Item>
              </Select.Content>
            </Select.Root>
          </Form.Control>
        </div>
      )}
    />
  </form>
</Form.Root>

Form.FieldComposed Props

PropTypeDefault
render*function—
name*string—
rulessee more—
shouldUnregisterboolean—
defaultValueany—
controlControl<TFieldValues, any, TFieldValues>—
disabledboolean—
exactboolean—
descriptionReactNode—
labelReactNode—
classNamestring—
variantenum"default"

FormRadix.Root Props

Root Form component from RadixUI. Provides form functionality and validation.

PropTypeDefault
onClearServerErrorsfunction—
classNamestring—
childrenReactNode—
asChildboolean—

Form.Root Props

PropTypeDefault
children*ReactNode | ReactNode[]—
watch*UseFormWatch<TFieldValues>—
getValues*UseFormGetValues<TFieldValues>—
getFieldState*UseFormGetFieldState<TFieldValues>—
setError*UseFormSetError<TFieldValues>—
clearErrors*UseFormClearErrors<TFieldValues>—
setValue*UseFormSetValue<TFieldValues>—
trigger*UseFormTrigger<TFieldValues>—
formState*FormState<TFieldValues>—
resetField*UseFormResetField<TFieldValues>—
reset*UseFormReset<TFieldValues>—
handleSubmit*see more—
unregister*UseFormUnregister<TFieldValues>—
control*see more—
register*UseFormRegister<TFieldValues>—
setFocus*UseFormSetFocus<TFieldValues>—
subscribe*UseFormSubscribe<TFieldValues>—

Form.FieldArrayRoot Props

PropTypeDefault
name*string—
keyNamestring"'id' as TKeyName"
controlControl<TFieldValues, any, TFieldValues>—
rulessee more—
shouldUnregisterboolean—
defaultValues*unknown—
children*function—
classNamestring—
fieldClassNamestring—
addButtonPropsany"{}"
addButtonChildrenReactNode"Add"
minnumber"0"
maxnumber"Number.MAX_SAFE_INTEGER"
hideCopyboolean—
hideMoveboolean—
labelReactNode—

Form.Control Props

PropTypeDefault
childrenReactNode—
classNamestring—

Form.Description Props

PropTypeDefault
classNamestring—
childrenReactNode—

Form.Field Props

PropTypeDefault
render*function—
name*string—
rulessee more—
shouldUnregisterboolean—
defaultValueany—
controlControl<TFieldValues, any, TFieldValues>—
disabledboolean—
exactboolean—

Form.FieldArrayControls Props

PropTypeDefault
count*number—
min*number—
hideCopyboolean"false"
hideMoveboolean"false"

Form.FieldArrayField Props

PropTypeDefault
render*function—
name*string—
rulessee more—
shouldUnregisterboolean—
defaultValueany—
controlControl<FieldValues, any, FieldValues>—
disabledboolean—
exactboolean—
classNamestring—
labelReactNode—

Form.Item Props

PropTypeDefault

Form.Label Props

PropTypeDefault
classNamestring—
childrenReactNode—
asChildboolean—

Form.Message Props

PropTypeDefault
classNamestring—
childrenReactNode—
errors{ message?: string; }[]—

FormRadix.Control Props

Form control primitive. Wraps form inputs and handles their state.

PropTypeDefault
typeenum—
classNamestring—
childrenReactNode—
asChildboolean—

FormRadix.Description Props

Form description component. Provides additional context or instructions for form fields.

PropTypeDefault
classNamestring—
childrenReactNode—

FormRadix.Field Props

PropTypeDefault
labelReactNode—
descriptionReactNode—
messagessee more—
name*string—
serverInvalidboolean—
classNamestring—
childrenReactNode—
asChildboolean—

FormRadix.Label Props

Form label component. Renders an accessible label for form controls.

PropTypeDefault
classNamestring—
childrenReactNode—
asChildboolean—

FormRadix.Message Props

Form message component for validation and error messages. Displays feedback about the form field state.

PropTypeDefault
matchsee more—
forceMatchboolean—
name*string—
classNamestring—
childrenReactNode—
asChildboolean—

FormRadix.Submit Props

Submit button primitive for forms.

PropTypeDefault
typeenum—
classNamestring—
childrenReactNode—
asChildboolean—

FormRadix.ValidityState Props

PropTypeDefault
children*function—
name*string—
__scopeFormScope—