Form composition with zod validation
I'm attempting to compose a form with optional components, e.g. in a given form there may or may not be a component that collects customer information. I'd like to add zod validation for each of these components, and then I'll add some conditional logic to render the 'blocks' as needed. I'm attempting to add a second of these components (rental period) but I'm running into some types trouble with the (fixed by not using
onChange
in the validatorz.union()
as well as passing the form
object as props to the form components.
Sorry if the image is confusing, I can add more context if needed. Disclaimer I'm pretty new to React and Tanstack, so I may be conflating terms. Thanks for any help.
16 Replies
variable-limeOP•3w ago
Maybe I misunderstood
union
. I put everything into one z.object()
and the validation works now.variable-limeOP•3w ago
I'm running into this error when passing the form object though. I'm not sure how to pass only part of the form to the component, which is what I assume the error is.

xenial-black•3w ago
You're looking for the new
formGroup
feature CC @Luca | LeCarbonator
(Luca is our resident form group expert and can help more here 🙂 )variable-limeOP•3w ago
Wow, that was some timing. I was able to piece it together from the PR and fix the type issues. Just need to sort out submitting the form and then I'll be able to add the rest of my form components 🙂
correct-apricot•3w ago
some advice before you start with groups:
if you have optional blocks (could be undefined, could be present), then the field groups should reflect that. Otherwise, TypeScript may not realize that you‘re allowed to use the field group, even if you did the correct conditional check
TL;DR conditional rendering is easier inside the field group than outside
variable-limeOP•3w ago
Thanks for the advice. So would I be passing props to these field groups that would control their rendering? As I'm attempting to map over a provided object that has what blocks should be available to render, because ordering will matter on the output form (I'm basically creating a form builder)
correct-apricot•3w ago
no, I just mean that this type inference may have problems
this is dangerous with field groups
this will cause a runtime error for
fields="foo"
because it needs to know that foo.X
is a thing
to generate the store and other stufffrozen-sapphire•3w ago
Interesting, I haven't seen that issue, but I'll watch for it. So far, it seems to be working. I'll remove my previous comment as not to cause issues. This is the only way I can get TS to not complain, though.
correct-apricot•3w ago
the null and undefined is fine
the
{} as
is not
field rendering probably works, but the group‘s store would‘ve definitely brokenfrozen-sapphire•3w ago
Right, but I can't pass an object to it unless I just parse the schema directly and add in filler properties for the ones required. I thought the types would tell is that those fields existed similar to withForm. Under withForm, it says
" // These values are only used for type-checking, and are not used at runtime" so maybe I'm misunderstanding that as well.
correct-apricot•3w ago
and the field groups have a different comment
frozen-sapphire•3w ago
Right, that says
"// These default values are not used at runtime, but the keys are needed for mapping purposes.
// This allows you to spread
formOptions
without needing to redeclare it."correct-apricot•3w ago
I guess this can be confusing
perhaps I‘ll rephrase it to an alert widget and remove the „values“ part
it‘s more correct, but at the cost of readability
frozen-sapphire•3w ago
Well, you said it would give runtime errors so that means the object itself is used at runtime, right? Maybe clarify that? Or maybe it makes sense to others and I'm the odd man out, wouldn't be the first time
So does that mean my object has to have ALL potential properties defaulted even if they're optional in that field group itself?
correct-apricot•3w ago
perhaps it's easier to explain with what's going on behind the scenes
for
group.store
, it checks for two things:
* is fields
a string? If so, use Object.keys(defaultValues)
and prefix them wtih fields
* if it's a field map, just use each string provided.
Notice how if you do {} as Type
, field maps likely still work, but fields="foo"
would never give you proper store values.
So the values of the defaultValues
are unused, but the keys
are required.frozen-sapphire•3w ago
That makes a lot more sense. Ok, got it. I'll just have to define the object at the top and null out the optional values. Thank you!