Form Composition + Object
Hey! Loving Form so far. I've come across a more complex use case that I'd love some help with. I have the following zod set up:
Now, the tricky aspect is that I'd like to just have one component control all of those fields. It's like smart google address form input where you type part of your address, it finds it, and the fills it out:
The problem is that the
errors
object is empty when theres an error with one of the inner portions. I could snoop around the field.form.errors
object to find what i need but that gets pretty messy. Is there a reason that address
does not receieve address1
, address2
, etc as part of the errors? Any workarounds?41 Replies
rare-sapphire•3mo ago
the reason it doesn‘t receive them is because zod‘s auto-generated path does not match the error structure you want. It generates the errors as far down as it can by default. Using a superRefine, you can control more easily where the path should end up
as for TSF, it doesn‘t group errors from all subfields into its own. Perhaps this changes in the future, but that‘s the behaviour at the moment.
rare-sapphire•3mo ago
Another solution for this problem is being worked on in this PR: https://github.com/TanStack/form/pull/1469
Feedback is welcome! Get in before it goes live :PepeThumbs:
GitHub
feat(react-form): Add
withFieldGroup
by LeCarbonator · Pull Requ...Todos
Implementation
Unit tests
Documentation
Fixes
This PR implements a variation of withForm that can be used to create form groups. This form group allows extending defaultValues and has n...
fascinating-indigoOP•3mo ago
Gotcha, that makes sense! So looks like this PR would also address my issue, I'd pass in all sub fields, but then render it as one input
Only thing I could suggest is having a similar usage to what I have above as a test case
rare-sapphire•3mo ago
Sounds good! If you can create a small implementation on stackblitz, we can use it as reference (and for testing)
It doesn't need to be reusable from the start, since you mentioned what the desired behaviour should be. The important thing is that there's a way to rewrite it using field groups and see if there's problems with using it between two forms
fascinating-indigoOP•3mo ago
Is there a way to base the stackblitz off of that PR or should I just provide a sample of my original message?
rare-sapphire•3mo ago
a sample of the original message would suffice! You are of course free to experiment with the PR too!
fascinating-indigoOP•3mo ago
So here is how far I've gotten: https://stackblitz.com/edit/tanstack-form-rbrpiymq?file=src%2Findex.tsx
Couple of things I'm stuck on:
- Linking up the group into the form
- Accessing the error object inside the group
Also some feedback:
- Would be nice if the Group could also be exposed via the useAppForm()
- When using the
fields
, passing it an object, they keys are pretty wonly (see image)
- Would be nice if we could skip defaultValues
and pass a generic insteadManthan Mallikarjun
StackBlitz
Form Simple Example (duplicated) - StackBlitz
Run official live example code for Form Simple, created by Tanstack on StackBlitz

rare-sapphire•3mo ago
the reason it's wonky is because you've passed the zod schema directly as value
which exposes all of its internal types, methods and what not
you probably meant to put the schema in the validator
rare-sapphire•3mo ago

rare-sapphire•3mo ago
cleans up the fields section

rare-sapphire•3mo ago
could you elaborate on "exposing the group through useAppForm"? What would the desired API look like?
fascinating-indigoOP•3mo ago
Would that be possible?
And any idea how i should access the erorr messages?
Fixed the rest, thank you for pointing that out 🙂
rare-sapphire•3mo ago
field groups specifically should be validator agnostic (since any form should be able to use them).
You'll want to have some way to narrow
unknown
into a type that you commonly use.
This is what I use for my Feedback
field component if you need some inspiration:
as for the fields, there's two ways to map:
* everything lives in a namespace (you have an object, the name will equal the DeepKey pointing to that object)
* your fields live at the top of the page or at different places (you renamed them to something you want)
In your case, it's the first option. So this shorthand will do:
<AddressFieldGroup form={form} fields="address" />
as for this, while it is technically possible, you actually lose information. These field groups, while meant for reusability, are bound to instances of forms rather than every single form.genetic-orange•3mo ago
I hope it's ok if I jump in here for a second. You mentioned this PR and I've been attempting to test it, but I'm not sure if I'm just doing something wrong. But I put together a hopfully simple reproduction of my issue. Field keeps giving me type errors. I'm wondering if it's because the group I'm attempting to use this with has nested arrays and objects and isn't a flat object.
I even tried to putting only three string properties in the field group object which showed up on the fields typing, but I think the type being inferred form my form group was still breaking the typescript. I've reverted back the group object to use my schema object. Hopefully this help pinpoint the bug or my error.
https://stackblitz.com/edit/vitejs-vite-oxzq9khp?file=src%2FForm%2FAttributes.tsx
rare-sapphire•3mo ago
thanks for testing it! I‘ll look at the stackblitz later today and see what the issue may be
it is a bit hard to decode with 2000+ lines of zod schemas. I'm not sure if they work well together
the reason appears to be because your group is
| undefined
which it strictly tries to refuse
technically type safe, but bad DX. I'll keep it in mind!genetic-orange•3mo ago
Ok, I've reduced it down to the relevant schema for this reproduction. Hopefully that can help narrow it down some more.
rare-sapphire•3mo ago
Here's a breakdown of what TSF inferred. The schemas are boiled down to single properties since it's not a nested issue, but a top-level one.
Now here's the issue: you can guard this properly, but TypeScript won't know it.
This will error still
fascinating-indigoOP•3mo ago
I see, the limitation makes sense. Even within
useAppForm
, the devleoper could've passed in a zod schema OR something else
The approach I would take in this case is having the file export the schema you should use (since my entire app is on Zod), so my last question is how do I access the specific error? Seems like I'll have to collate them myself, but it would be nice if there was a group.getAllErrors()
function that only returns the errors that are scoped to that grouprare-sapphire•3mo ago
well, that function isn‘t reactive, so it wouldn‘t work well for the purpose you likely want it to
but perhaps an errors object in the store could be useful … you often want the fields grouped after all
fascinating-indigoOP•3mo ago
Sorry, I dont follow. Why wouldn't that work well? See https://stackblitz.com/edit/tanstack-form-rbrpiymq?file=src%2Findex.tsx,src%2Fapp-form.tsx for an example usage
The error type (value) wouldn't be defined as the validator could be anything, but the keys should be known
rare-sapphire•3mo ago
try actually triggering errors. I would be surprised if it live updated every time
with reactive, I meant that it would run once and then stay that way
I just realized that the function isn‘t a thing, so it can‘t be demonstrated … Perhaps it‘s best if I take a proper look tomorrow instead
fascinating-indigoOP•3mo ago
Oh yeah, its not real. Just what I would've intuitively expected with a form group
genetic-orange•3mo ago
Yep, I see. I took away the .optional and it didn't give any errors. That's not the intended final design is it that the object has to be required in the parent schema?
rare-sapphire•3mo ago
the compromise will likely be that
fields
accepts a string of the expected shape, null or undefined. I don‘t see form mapping to be compromised for this
if the expected values don‘t actually exist, you risk runtime errors very quickly with this onegenetic-orange•3mo ago
Awesome. Yeah I can understand the expected object to contain only values that could exist. That makes perfect sense. I just wanted to make sure the group object was able to be an optional object on the parent. Thanks!
rare-sapphire•3mo ago
what‘s good is if the namespace can‘t be resolved, the field map can pinpoint the problem
like for your case, the __type was declared the missing type that cannot be resolved since it always expects to be around
which the field map did show
genetic-orange•3mo ago
I don’t quite follow what you mean but I’ll go back and look at it and see what the error was showing
rare-sapphire•3mo ago
the map shows all keys and all deepkeys that evaluate to the right type. So if you need to debug why a field group doesn‘t work, the map helps you pinpoing which property resolved to never
genetic-orange•3mo ago
@nahtnam Sorry to hijack your thread. If you want, I can start a new one. I just figured it was relevant since we're talking about the withFieldGroup. You just let me know. @Luca | LeCarbonator Here's what I see in the stackblitz at least. Are you saying it's the __kind field that's causing the issue?

rare-sapphire•3mo ago
yes, because it's a required field that must be a literal string
genetic-orange•3mo ago
Sorry to keep asking. I'm still trying to understand what is probably intuitive to most others. Is that because the group object is optional in my parent object or is it because I didn't default that __kind property in the group object?
rare-sapphire•3mo ago
it's because the group object is optional in your parent object.
The reason the other properties are assignable is because the damage input object itself doesn't require them either
genetic-orange•3mo ago
Got it. Hmm, ok. Not sure how I'd fix that since that object being required is based on other form inputs.
rare-sapphire•3mo ago
yeah, as mentioned above, it's type safe but not good DX. If it's optional in the form, then chances are it's in its own object, so this is probably going to be how it works
genetic-orange•3mo ago
Ahhh gotcha. Ok, thanks!
Also, if what I'm doing isn't a normal strategy, I'm ok being told that. If there's a better way to do what I'm trying to do, I'm all ears.
rare-sapphire•3mo ago
dynamic fields are fairly common, so you're fine
genetic-orange•3mo ago
@Luca | LeCarbonator I'm getting pretty far along using this withFieldGroup, but I do have a question. When I try to use the group.removeFieldValue method, it types the field as never if the property is optional. I know I can just create a new array with the item removed and then do setFieldValue, I just wasn't sure if that was intentional behavior with the removeFieldValue function.
rare-sapphire•3mo ago
well, it requires an array as to not throw an error
it manages meta shifting for you. Why is the array optional, may I ask?
genetic-orange•3mo ago
Much of the data is optional, really. I'm not using SQL, rather GraphQL. So, while I have a schema defined, not all fields are required. This is a fairly complex form for user content creation for TTRPG content. So there's a lot of variety even within a group of what's actually required. And rather than filling in empty arrays automatically to just have to disregard them when the data is submitted, I just leave it optional. Like I said, I can use setFieldValue, it's just one extra line so not really an issue more just wanting to know for my own knowledge.
rare-sapphire•3mo ago
this … is a solid use case. Could you make a github issue to request optional arrays to have array methods?
including the use case
genetic-orange•3mo ago
Yep, I can do that