Skip to Content
DocumentationFormsOverview

Forms

Zen provides a complete form system built on react-hook-form . The Form and FormField components handle validation, error display, and field state management with minimal boilerplate.

How it works

The form system consists of several components that work together:

  • Form - Wraps your form and provides react-hook-form context
  • FormField - Connects any input component to the form with automatic value binding and validation
  • FormButtons - Container for form action buttons
  • FormSubmitButton - Submit button with automatic loading and disabled states
  • FormResetButton - Reset button that clears the form to default values

Basic usage

Wrap your inputs with FormField components inside a Form. Each FormField needs a name that corresponds to a key in your form data.

Validation

Add validation rules to FormField using the rules prop. This uses react-hook-form’s validation API.

Common validation rules

RuleDescription
requiredField must have a value
minMinimum value for numbers
maxMaximum value for numbers
minLengthMinimum string length
maxLengthMaximum string length
patternRegex pattern to match
validateCustom validation function

Loading states

FormSubmitButton automatically shows a loading spinner and disables itself while the form is submitting. This happens when your onSubmit handler returns a Promise.

The submit button is automatically disabled when:

  • The form has not been modified (isDirty is false)
  • The form has validation errors (isValid is false)
  • The form is currently submitting (isSubmitting is true)

You can override this behavior with the isDisabled prop.

Error handling

Pass an error prop to Form to display an error banner above the form. This is useful for showing server-side validation errors or API failures.

Invalid credentials. Please try again.

All form inputs

FormField works with all Zen input components. The field automatically binds the value and onChange props.

0
One
Two
Three
Four
Five

Dynamic fields with FormFieldArray

Use FormFieldArray when you need to manage a list of fields that can be added or removed dynamically.

Accessing form state

The Form component accepts a render function as children, giving you access to the full react-hook-form API.

<Form defaultValues={{ name: '' }} onSubmit={handleSubmit}> {({ watch, formState }) => ( <> <FormField name="name" label="Name"> <TextField /> </FormField> <Text>Current value: {watch('name')}</Text> <Text>Form is dirty: {formState.isDirty ? 'Yes' : 'No'}</Text> <FormButtons> <FormSubmitButton variant="primary">Submit</FormSubmitButton> </FormButtons> </> )} </Form>

Schema validation

Use the resolver prop to integrate with schema validation libraries like Zod or Yup.

import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; const schema = z.object({ email: z.string().email('Invalid email'), password: z.string().min(8, 'Password must be at least 8 characters'), }); function MyForm() { return ( <Form resolver={zodResolver(schema)} onSubmit={handleSubmit} > <FormField name="email" label="Email"> <TextField /> </FormField> <FormField name="password" label="Password"> <PasswordField /> </FormField> <FormButtons> <FormSubmitButton variant="primary">Submit</FormSubmitButton> </FormButtons> </Form> ); }

Controlled forms

Use the values prop instead of defaultValues when you need to control form values externally. The form will reset when the values change.

function EditUserForm({ user }) { return ( <Form values={{ name: user.name, email: user.email }} onSubmit={handleSubmit} > <FormField name="name" label="Name"> <TextField /> </FormField> <FormField name="email" label="Email"> <TextField /> </FormField> <FormButtons> <FormSubmitButton variant="primary">Save</FormSubmitButton> </FormButtons> </Form> ); }

Preventing enter key submission

Use preventSubmit to stop the form from submitting when the user presses Enter. Useful for forms where you want explicit button clicks only.

<Form preventSubmit onSubmit={handleSubmit}> <FormField name="search" label="Search"> <TextField /> </FormField> <FormButtons> <FormSubmitButton variant="primary">Search</FormSubmitButton> </FormButtons> </Form>

Component props

Form

NameTypeDescription
defaultValuesobjectInitial form values (uncontrolled)
valuesobjectCurrent form values (controlled)
onSubmit(data) => void | PromiseCalled when form is submitted with valid data
errorReactNode | ErrorError message to display above the form
preventSubmitbooleanPrevents form submission on Enter key
resolverResolverValidation resolver for schema validation (Zod, Yup, etc.)
modestringValidation mode: "onSubmit" | "onBlur" | "onChange" | "onTouched" | "all"

FormField

NameTypeDescription
namestringField name (required). Must match a key in form values.
labelstringLabel displayed above the input
descriptionstringHelp text displayed below the input
rulesRegisterOptionsValidation rules (required, min, max, pattern, validate, etc.)

FormFieldArray

NameTypeDescription
namestringField array name (required)
labelstringLabel displayed above the field array
descriptionstringHelp text displayed below the label
children(props) => ReactNodeRender function receiving fields, append, remove, and control

FormSubmitButton

NameTypeDescription
variantButtonVariantButton style variant (default: "primary")
isDisabledbooleanOverride automatic disabled state
isLoadingbooleanOverride automatic loading state

FormResetButton

NameTypeDescription
valuesobjectValues to reset the form to (default: original defaultValues)