To refresh your memory, Formik uses an error shape like so
{ name: "No name provided", "age": "You must be at least 18 years old" }
I love formik, but I think it is a design flaw that its default errors shape only allows for one error per field. You might have multiple errors at one field.
We can solve that by using zod's error approach of using an array with paths. The same errors could look like this:
[
{ path: ["name"], code: "too_small", message: "Should be at least 1 characters" },
{ path: ["age"], code: "too_small", message: "Value should be greater than or equal to 18" },
]
Something that's hard in Formik is if your error can appear at a nested field or above it, as the default errors shape can't handle that.
Imagine a list of guests where each guest must have name, and where you must invite at least three guests.
{ guests: [{ name: "jon" }, { name: "" }] }
I couldn't find a way/it seems very hard/not possible to handle this case with Formik's own shape, although I found that this comes up quite a bit.
With zod's errors shape this is trivial:
[
{ path: ["guests"], code: "custom_error", message: "Must invite at least three guests" },
{ path: ["guests", 1, "name], code: "too_small", message: "Should be at least 1 characters" },
]
By changing the shape from Formik's default to an array of paths (an array of z.Suberror
to be exact), we can deal with errors at all levels easily.
We can add helper functions to our form library to deal with these arrays of errors.
zodForm.errors
is an array of z.Suberror
, so it looks like this
[{ path: ["name"], code: "custom_error", message: "some custom message" }]
To check if a field has an error, you can do
zodForm.hasErrors({ path: ["name"] })
This will return all errors at that path or its subpaths but you can also restricit it to errors of that exact path only like so:
zodForm.hasErrors({ path: ["name"] }, { exact: true })
What's super useful is that you can query your errors:
zodForm.hasErrors({ path: ["name"], code: "custom_error", message: "some custom message" })
// returns true if there is at least one error at the path (or its subpath) with the provided values for code and message.
The same API works for receiving those errors
zodForm.getErrors({ path: ["name"], code: "custom_error", message: "some custom message" })
// returns all errors whose path starts with "name" and who'se code and message match the provided values.