edmundhung / conform

A type-safe form validation library utilizing web fundamentals to progressively enhance HTML Forms with full support for server frameworks like Remix and Next.js.
https://conform.guide
MIT License
1.8k stars 101 forks source link

schema not allowing empty string through #676

Closed lifeiscontent closed 3 days ago

lifeiscontent commented 3 months ago

Describe the bug and the expected behavior

https://stackblitz.com/edit/remix-run-remix-fd6e6f?file=app%2Froutes%2F_index.tsx,app%2Fentry.client.tsx

I'd expect being able to press "Submit" to work because the default value of inputs is ""

Conform version

v.1.1.4

Steps to Reproduce the Bug or Issue

  1. Goto the stackblitz page
  2. press enter
  3. see validation error instead of submission

What browsers are you seeing the problem on?

No response

Screenshots or Videos

No response

Additional context

No response

edmundhung commented 2 months ago

This is working by design. You can find an explanation here.

lifeiscontent commented 2 months ago

@edmundhung in order to support empty strings I've had to use z.string().optional().default("__EMPTY_STRING__").transform(value => typeof value === undefined || value === "__EMPTY_STRING__" ? "" : value);

this seems like a bit of a footgun, any ideas on how to make this a bit easier to handle?

edmundhung commented 1 month ago

@edmundhung in order to support empty strings I've had to use z.string().optional().default("__EMPTY_STRING__").transform(value => typeof value === undefined || value === "__EMPTY_STRING__" ? "" : value);

Is the .default() necessary? I believe .transform(value => value ?? '') would be enough

lifeiscontent commented 1 month ago

@edmundhung you're right, is there a reason why z.string() works differently than other types?

I'd expect z.string().optional().default('') to work as well, since it works with all other types too such as z.number, z.boolean, etc

edmundhung commented 4 weeks ago

It is because .default() runs before the coercion and so the empty string will be stripped regardless. But I can see how the inconsistency here will trap many people.

I can add additional logic to handle this case. But I wonder if .default('') is really what we want here. As the default value will still be validated. If you have a email schema like z.string().email().default(''), you will end up with a invalid email error. While z.string().email().optional().transform(value => value ?? '') would not have such issue.

lifeiscontent commented 4 weeks ago

@edmundhung I believe in the case where you have a larger schema that has a union the default will still pass the object validation and then the individual email check will happen, you can see an example of this in the schema I sent you on discord.