Form
/ Guides
Form
/ Guides

Complex Forms & Performance

Sometimes, a single top-level useForm hook can be cumbersome to manage and/or scale.
The way hooks in React work is that they rerender the parent component every time the hook state changes. For simple forms with a couple of fields, this usually works fine, but once we build more complex forms with multiple nested components, this can become a performance bottleneck.
On top of that, extracting parts of the form into separate components can be cumbersome due to how the API is designed.

To mitigate both issues, we can use the createForm helper to create a form utils from a Zod schema including a scoped useForm hook, a form context provider and a controlled field component.
It ensures that no unnecessary rerenders are triggered when the form state changes and allows us to extract parts of the form into separate components.

import { z } from 'zod'
import { createForm } from '@weser/form'

const Z_RegisterInput = z.object({
  name: z.string().optional(),
  email: z.string().email(),
  password: z.string().min(8),
})

const { useForm, FormProvider, Field, useFormContext } =
  createForm(Z_RegisterInput)

function RegisterForm() {
  const form = useForm()

  function onSuccess(data: T_RegisterInput) {
    console.log(data)
  }

  function onFailure(error: ZodError) {
    console.error(error)
  }

  return (
    <FormProvider value={form}>
      <form onSubmit={form.handleSubmit(onSuccess, onFailure)}>
        <Field name="name">{(field) => <input {...field.inputProps} />}</Field>
        <Field name="email">
          {(field) => <input type="email" {...field.inputProps} />}
        </Field>
        <Field name="password">
          {(field) => <input type="password" {...field.inputProps} />}
        </Field>
        <button type="submit">Sign up</button>
      </form>
    </FormProvider>
  )
}

Using the Form Context

The FormProvider is a context provider that provides the form utils to its children.
It allows us to use the useFormContext hook to access e.g. isValidating or useFormField from nested components.

function ValidationIndicator() {
  const { isValidating } = useFormContext()

  return isValidating ? <p>Validating...</p> : null
}
On this page