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>
    <Form.FieldArrayField>
      <Form.Label />
      <Form.Control />
      <Form.FieldArrayRemove />
    </Form.FieldArrayField>
    <Form.FieldArrayAdd />
  </Form.FieldArray>
</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
descriptionReactNode
labelReactNode
classNamestring

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 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.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

Form.FieldArray Props

PropTypeDefault
name*string
keyNamestring
controlControl<TFieldValues, any, TFieldValues>
rulessee more
shouldUnregisterboolean
defaultValues*unknown
children*function
classNamestring
fieldClassNamestring
addButtonPropsany
addButtonChildrenReactNode
minnumber
maxnumber
hideCopyboolean
hideMoveboolean
labelReactNode

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
classNamestring
labelReactNode

Form.Item Props

PropTypeDefault
classNamestring
childrenReactNode

Form.Label Props

PropTypeDefault
classNamestring
childrenReactNode
asChildboolean

Form.Message Props

PropTypeDefault
classNamestring
childrenReactNode

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