abexo's avatar

Handling Empty Strings with Zod and AutoForm

Lately, I was working on a project using Vue, Inertia, Zod, and AutoForm, and I ran into one of those situations that gave me way more trouble than it should have (also my inexperience with zod and maybe AutoForm is a bit buggy).

Whenever I typed something into a form field and then deleted it, the field’s value would become an empty string.

So what i finally did was something like this

z.preprocess(
  (val) => (val === '' ? null : val),
  z.string().nullable()
).optional();

If you’re stuck wondering why your Zod validation fails after clearing a form input, this might be it.

Also If anyone is doing this in a different way, feel free to share it! I’d love to learn how others are handling this.

Thanks

1 like
1 reply
LaryAI's avatar

Great question! This is a common issue when working with forms in the frontend, especially with libraries like Zod that distinguish between '', null, and undefined.

Your approach using z.preprocess is a solid and idiomatic way to handle this with Zod. It ensures that empty strings from your form are converted to null before validation, which is often what you want for optional or nullable fields.

Here’s a quick recap and a couple of alternative approaches you might find useful:

1. Your Approach (Recommended)

const schema = z.preprocess(
  val => val === '' ? null : val,
  z.string().nullable()
).optional();

This will treat empty strings as null, and the field is both nullable and optional.


2. Using .transform (If You Want to Always Get a String or Null)

If you want to ensure the output is always a string or null, you can use .transform:

const schema = z.string()
  .transform(val => val === '' ? null : val)
  .nullable()
  .optional();

But note: .transform runs after validation, so if the value is '' and you don’t allow empty strings, validation will fail before transform runs. That’s why preprocess is usually better for this use case.


3. Custom Refinement (Not recommended for this use case)

You could use .refine, but it’s more verbose and less idiomatic for this scenario.


4. Handling at the Form Level

Alternatively, you could normalize your form data before passing it to Zod, converting all empty strings to null or undefined. This can be useful if you have many fields and want to apply the same logic everywhere.

function normalizeFormData(data) {
  return Object.fromEntries(
    Object.entries(data).map(([key, value]) => [key, value === '' ? null : value])
  );
}

Summary

  • Your solution using z.preprocess is the most idiomatic and flexible for Zod.
  • If you have many fields, consider normalizing the data before validation.
  • Avoid using .transform for this, unless you’re sure about the validation order.

Let me know if you have more questions or want to see how to apply this to an entire form schema!

Please or to participate in this conversation.