Published on

How to work with Shadcn/ui error messages for an array of FormFields

Authors
  • Name
    Ripal & Zalak
    Twitter

Managing error messages for array-based form fields in Shadcn/UI can be tricky, especially when combined with React-Hook-Form and Zod validation. This guide walks you through implementing a solution to display error messages for an array of form fields efficiently.

Problem Statement

When working with dynamic form arrays, such as steps in a process, displaying validation errors in the right place without cluttering the UI can be challenging. If you naively attach an error message to each field, it can overwhelm the user.

Here’s an example scenario:

  • You have a dynamic form that lets users add or remove steps.
  • Each step includes a title and a date field.
  • You want to display error messages below the array only once, rather than duplicating them for every field.

Example Form Implementation

Here is a base implementation of a dynamic form with error messages using Shadcn/UI:

<div className="mb-10">
  <div className="flex flex-col gap-2">
    {stepFields.map((field, i) => (
      <FormField
        key={field.id}
        name={`steps[${i}].title`}
        render={() => (
          <FormItem>
            {!i && (
              <FormLabel>
                <h2 className="mb-2 text-xl font-semibold">
                  Can you list the key steps you need to take?
                </h2>
              </FormLabel>
            )}
            <div className="flex gap-2">
              <Button
                variant={'ghost'}
                onClick={() => removeStep(i)}
                disabled={stepFields.length === 1}
              >
                <LuTrash2 className="h-5 w-5" />
              </Button>
              <FormControl>
                <div className="flex w-full gap-2">
                  <Input
                    {...form.register(`steps.${i}.title`, {
                      required: true,
                    })}
                    placeholder={`Step ${i + 1}`}
                    className="w-full bg-zinc-50 p-2"
                  />
                  <Input
                    {...form.register(`steps.${i}.date`, {
                      required: true,
                    })}
                    type="date"
                  />
                </div>
              </FormControl>
            </div>
            {i + 1 === stepFields.length && (
              <>
                <Button
                  variant={'secondary'}
                  className="w-full border"
                  onClick={() => addStep({ title: '', date: new Date() })}
                  type="button"
                >
                  Add step
                </Button>
                <FormMessage />
              </>
            )}
          </FormItem>
        )}
      />
    ))}
  </div>
</div>

Key Points in the Implementation

  1. Error Messages Below the Array:

    • The FormMessage component is placed outside the individual fields but inside the loop, ensuring it appears only once.
  2. Dynamic Heading and Add Button Visibility:

    • The heading and add button remain visible even when the array length is 1, preventing the UI from collapsing.
  3. Remove Button Logic:

    • The remove button is disabled when the array length is 1, ensuring at least one step remains visible.

Simplified Error Handling Logic

To ensure error messages appear only once:

  • Place the <FormMessage /> outside the field loop but inside the parent <FormItem />.

  • Use conditional rendering with index checks:

    {
      !i && <FormLabel>Array Label</FormLabel>
    }
    {
      i + 1 === stepFields.length && <FormMessage />
    }
    

FAQs

1. Why do I need conditional rendering for labels and messages?

Conditional rendering prevents duplication and ensures a clean, user-friendly interface.

2. Can this approach handle deeply nested arrays?

Yes, with modifications to the name and validation schema, you can handle nested arrays.

3. How does React-Hook-Form interact with Zod?

React-Hook-Form leverages Zod for schema validation, making it easier to define and enforce rules on your form data.

Conclusion

By structuring your form fields and error handling carefully, you can create a robust and user-friendly experience with Shadcn/UI. Use the tips and examples provided here to streamline your array-based form field management.

For more insights, check out the official Shadcn/UI documentation and React-Hook-Form resources.