T
TanStack•4w ago
foreign-sapphire

Question about TanStack Form `formOptions` vs regular object

Hey everyone! đź‘‹ I'm trying to understand the practical difference between using formOptions() vs just exporting a regular object with the same structure in TanStack Form. Using formOptions():
interface User {
firstName: string
lastName: string
hobbies: Array<string>
}
const defaultUser: User = { firstName: '', lastName: '', hobbies: [] }

const formOpts = formOptions({
defaultValues: defaultUser,
// other options...
})

const form = useForm({
...formOpts,
onSubmit: async ({ value }) => {
console.log(value)
},
})
interface User {
firstName: string
lastName: string
hobbies: Array<string>
}
const defaultUser: User = { firstName: '', lastName: '', hobbies: [] }

const formOpts = formOptions({
defaultValues: defaultUser,
// other options...
})

const form = useForm({
...formOpts,
onSubmit: async ({ value }) => {
console.log(value)
},
})
vs regular object:
interface User {
firstName: string
lastName: string
hobbies: Array<string>
}
const defaultUser: User = { firstName: '', lastName: '', hobbies: [] }

const formOpts = {
defaultValues: defaultUser,
// other options...
}

const form = useForm({
...formOpts,
onSubmit: async ({ value }) => {
console.log(value)
},
})
interface User {
firstName: string
lastName: string
hobbies: Array<string>
}
const defaultUser: User = { firstName: '', lastName: '', hobbies: [] }

const formOpts = {
defaultValues: defaultUser,
// other options...
}

const form = useForm({
...formOpts,
onSubmit: async ({ value }) => {
console.log(value)
},
})
What are the actual benefits/differences of using the formOptions() function? Is it just for type safety, or does it provide additional functionality under the hood? My original thought was that it would allow you to add defaultValues, validators, etc all in one place—and then export that use it across your app (using form composition). But then I saw this Discord thread, and this GitHub Issue —so I’m slightly confused. I also think it might be helpful if the docs explained why you would go one route over the other (as it shows using formOptions and a regular object). I'm also curious about when using formOptions with withForm - especially when the form options include validators. Does formOptions() provide better type inference or validation handling when passed to withForm compared to a regular object with the same validator functions? Reference: TanStack Form Docs - Form Options Thank you! 🙏
3 Replies
foreign-sapphire
foreign-sapphire•4w ago
Well, with the current implementation, it's the difference between what it's supposed to be and what it currently is: * It's supposed to be a type-safe way to create form options that can be shared between multiple forms. The use case you mentioned with withForm, for example - If you added another validator to the main component, then the withForm would no longer be in sync and throw a type error. If you instead used a formOptions object, you can change it there and affect all required locations it's used in. * What it currently is is lacking type safety. The main reason why is because TypeScript becomes eager and wants to be more strict with generics if it worked exactly as useForm.
const oops = formOptions({
defaultValues: { a: '' },
validators: {
// Currently: Type error, it wrongly thinks this is 'any'
onChange: ({ value }) => value.a
}
})

// However, if the above error was 'fixed' using generics that are the same as useForm, then it would error here:
const form = useForm({
...commonFormOpts,
// but in this case, I want to overwrite something
validators: {
onSubmit: someSchema
}
// this now errors because TypeScript strictly expects `validators` to already be defined from `commonFormOpts`,
// preventing a lot of overwriting properties.
})
const oops = formOptions({
defaultValues: { a: '' },
validators: {
// Currently: Type error, it wrongly thinks this is 'any'
onChange: ({ value }) => value.a
}
})

// However, if the above error was 'fixed' using generics that are the same as useForm, then it would error here:
const form = useForm({
...commonFormOpts,
// but in this case, I want to overwrite something
validators: {
onSubmit: someSchema
}
// this now errors because TypeScript strictly expects `validators` to already be defined from `commonFormOpts`,
// preventing a lot of overwriting properties.
})
Regular objects sort of fall in the same category as the current implementation. They won't give you much type safety when creating it, but when spreading the options, it should warn you as expected. Until this is addressed, there won't be much of a difference.
foreign-sapphire
foreign-sapphireOP•4w ago
Thanks (as always), Luca! When it comes to using formOptions with withForm, is it cool if those formOptions also have validators (or things like onSubmit, etc)—or is withForm just looking for defaults?
foreign-sapphire
foreign-sapphire•4w ago
withForm is looking for defaults and validators it ignores things like onSubmit or onSubmitInvalid though ... oh dear, that actually brings up something important for a future feature. Good thing I just had to bring it up!

Did you find this page helpful?