remix-validated-form: [Bug]: `zfd.checkbox()` should require user to check by default
zfd.checkbox() should mean the field is required (e.g accept terms and conditions) to follow zod’s pattern
Because your Union transforms undefined into false, technically this is a valid value:
export const checkbox = ({ trueValue = "on" }: CheckboxOpts = {}) =>
z.union([
z.literal(trueValue).transform(() => true),
z.literal(undefined).transform(() => false),
]);
I don’t believe the union is necesarry at all, as undefined is the same as false during checks.
If you skip that transform we can zfd.checkbox().optional()
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 34 (29 by maintainers)
I don’t really agree with this. I want
zfd.checkbox()to be analogous toz.boolean(). If the checkbox is checked it should betrue, and if the checkbox is unchecked then it should befalse. And likez.boolean(), we shouldn’t make either value fail the validation by default.For better or worse, an unchecked checkbox is omitted from the
FormDatawhen the form is submitted. To me this means there’s no meaningful way to useoptionalsince there’s no way for us to tell the difference between “unchecked” and “there is no checkbox”.That said, you can enforce that a checkbox is checked with
refine.In the interest of not spiraling on this forever I think this is going to have to be my last comment on this issue. I’ve outlined in this comment what I see as the path forward for this.
I think this library should take form data and return properly typed and well modeled representation of the data in the form. From a data modeling perspective,
booleanis a more accurate representation of a checkbox thantrue | undefined.true | undefinedis also a “boolean”, it’s just not abooleanwhich makes it confusing.If we don’t transform the value to a boolean, then why have a helper at all? It sounds like your ideal version of
checkboxundefinedby defaultBut that’s the exact behavior of
z.string.The purpose of having a helper for checkboxes at all, for me, is to get that coercion to happen automatically.
I’m also not super concerned about
optionalnot doing anything oncheckbox. zod surfaces theoptionalmethod even on schemas where it doesn’t make sense to have it (e.g. it’s valid to doz.string().optional().optional()and the second optional does nothing).The state of a checkbox is also a boolean (checked vs not checked), even if it’s not encoded as a boolean type. And it can be checked even if the user hasn’t interacted with it (can default to checked).
What I’m ultimately trying to get at with
zfd.checkboxis “is the checkbox checked, true or false?”. It’s up to the developer to make the judgement on a per-form basis if the user is required to check that checkbox or not. For the vast majority of forms I’ve written, both checked an unchecked were totally valid. Every once in awhile you have a form with a checkbox likeacceptsLicenseAgreementwhere unchecked is invalid in that particular case.