Form & FormField

Form components with built-in state management, validation, and field binding. Automatically handles value updates, error display, and accessibility.

Form ArchitectureContext-based

Form provides context for state management and validation. FormField wraps input controls and automatically connects them to the form context.

Form handles:
  • Values state
  • Errors state
  • Touched state
  • Validation
  • Submit handling
FormField handles:
  • Value binding
  • Error display
  • Label rendering
  • Helper text
  • Accessibility

Basic Form

Submitted Values

Submit the form to see values here

JSX
<Form
  initialValues={{ name: '', email: '', message: '' }}
  validate={(values) => {
    const errors: FormErrors = {};
    if (!values.name) errors.name = 'Name is required';
    if (!values.email) errors.email = 'Email is required';
    else if (!/\S+@\S+\.\S+/.test(values.email)) {
      errors.email = 'Invalid email format';
    }
    if (!values.message) errors.message = 'Message is required';
    return errors;
  }}
  onSubmit={(values) => console.log(values)}
>
  {({ isSubmitting }) => (
    <>
      <FormField name="name" label="Name" required>
        <Input placeholder="Enter your name" />
      </FormField>

      <FormField name="email" label="Email" required helperText="We'll never share your email">
        <Input type="email" placeholder="you@example.com" />
      </FormField>

      <FormField name="message" label="Message" required>
        <Textarea placeholder="Your message..." autoResize minRows={3} />
      </FormField>

      <Button type="submit" variant="primary" isLoading={isSubmitting}>
        Send Message
      </Button>
    </>
  )}
</Form>

Validation Modes

Control when validation runs with validateOnChange and validateOnBlur props.

JSX
// Validate on every change (default)
<Form validateOnChange={true} validateOnBlur={true} ...>

// Validate only on blur
<Form validateOnChange={false} validateOnBlur={true} ...>

// Validate only on submit
<Form validateOnChange={false} validateOnBlur={false} ...>

Async Validation

The validate function can return a Promise for async validation (e.g., checking username availability).

JSX
<Form
  initialValues={{ username: '' }}
  validate={async (values) => {
    const errors: FormErrors = {};

    if (!values.username) {
      errors.username = 'Username is required';
    } else {
      // Simulate API check
      const isAvailable = await checkUsernameAvailability(values.username);
      if (!isAvailable) {
        errors.username = 'Username is already taken';
      }
    }

    return errors;
  }}
  onSubmit={handleSubmit}
>
  <FormField name="username" label="Username" required>
    <Input placeholder="Choose a username" />
  </FormField>
  <Button type="submit">Register</Button>
</Form>

Label Positions

FormField supports different label positions: top (default), left, and right.

JSX
// Label above input (default)
<FormField name="email" label="Email" labelPosition="top">
  <Input />
</FormField>

// Label to the left
<FormField name="email" label="Email" labelPosition="left">
  <Input />
</FormField>

// Label to the right (useful for checkboxes)
<FormField name="agree" label="I agree to terms" labelPosition="right">
  <Checkbox />
</FormField>

// Hidden label (still accessible via screen readers)
<FormField name="search" label="Search" hideLabel>
  <Input placeholder="Search..." leftIcon={<Icon name="search" size="sm" />} />
</FormField>

Form Render Props

Form accepts children as a render function, providing access to form state and methods.

JSX
<Form initialValues={...} onSubmit={...}>
  {({
    values,         // Current form values
    errors,         // Current validation errors
    touched,        // Which fields have been touched
    isSubmitting,   // Whether form is being submitted
    isValid,        // Whether form has no errors
    setFieldValue,  // Manually set a field value
    setFieldError,  // Manually set a field error
    setFieldTouched,// Manually mark a field as touched
    getFieldProps,  // Get binding props for a field
  }) => (
    <div>
      {/* Conditionally show submit button */}
      <Button
        type="submit"
        disabled={!isValid || isSubmitting}
        isLoading={isSubmitting}
      >
        Submit
      </Button>

      {/* Show current values for debugging */}
      <pre>{JSON.stringify(values, null, 2)}</pre>
    </div>
  )}
</Form>

Props Reference

Form

PropTypeDefaultDescription
initialValuesTrequiredInitial form values object
onSubmit(values: T) => void | PromiserequiredSubmit handler
validate(values: T) => FormErrors | Promise-Validation function
validateOnChangebooleantrueValidate on value change
validateOnBlurbooleantrueValidate on field blur
preventDefaultbooleantruePrevent default form submission

FormField

PropTypeDefaultDescription
namestringrequiredField name (matches form values key)
labelReactNode-Field label
helperTextReactNode-Helper text below field
requiredbooleanfalseShow required indicator
labelPosition'top' | 'left' | 'right''top'Label position relative to input
hideLabelbooleanfalseHide label visually (still accessible)