optional-form-field
EditMakes any Zod schema optional while mapping an exact empty form value to undefined.
import * as z from "zod";
/** * Creates an optional schema that treats an exact empty string as `undefined`. * * Other values, including whitespace-only strings, are passed to the supplied * schema unchanged so that it remains in control of validation and transforms. */export const optionalFormField = <Schema extends z.ZodType>( schema: Schema,): z.ZodType< z.output<Schema> | undefined, z.input<Schema> | "" | undefined> => z.preprocess( (value: z.input<Schema> | "" | undefined) => value === "" ? undefined : value, schema.optional(), ) as z.ZodType< z.output<Schema> | undefined, z.input<Schema> | "" | undefined >;Download
Section titled “Download”wget -O src/schemas/optional-form-field.ts https://raw.githubusercontent.com/jrTilak/lazykit/HEAD/registry/zod-schemas/optional-form-field.tstemp_dir="$(mktemp -d)" && bunx degit jrTilak/lazykit/registry/zod-schemas "$temp_dir" && mv "$temp_dir/optional-form-field.ts" src/schemas/optional-form-field.ts && rm -r "$temp_dir"Examples
Section titled “Examples”import * as z from "zod";
import { optionalFormField } from "./optional-form-field";
const profileFormSchema = z.object({ displayName: z.string().trim().min(1), bio: optionalFormField(z.string().trim().max(160)), website: optionalFormField(z.httpUrl()), notifications: optionalFormField( z.object({ frequency: z.enum(["daily", "weekly"]), }), ),});
const profile = profileFormSchema.parse({ displayName: " Ada ", bio: "", website: "https://example.com", notifications: { frequency: "weekly", },});
// {// displayName: "Ada",// bio: undefined,// website: "https://example.com",// notifications: { frequency: "weekly" }// }console.log(profile);
const invalid = profileFormSchema.safeParse({ displayName: "Ada", notifications: { frequency: "monthly", },});
if (!invalid.success) { // ["notifications", "frequency"] — the inner schema's direct issue path. console.log(invalid.error.issues[0]?.path);}
export type ProfileFormInput = z.input<typeof profileFormSchema>;export type Profile = z.output<typeof profileFormSchema>;schema(Schema extends z.ZodType) — The schema used for every non-empty value.
Returns
Section titled “Returns”A schema with input type z.input<Schema> | "" | undefined and output type
z.output<Schema> | undefined. Zod 4 preprocessing maps the exact empty string
"" to undefined before the optional inner schema runs. An already
undefined value follows the same absent-value path.
Whitespace-only strings are not considered empty. All other values are passed
to the supplied schema unchanged, preserving its validation, transforms,
asynchronous parsing, and output type. Inner failures remain direct Zod issues
with their original codes and nested paths instead of being wrapped in an
invalid_union issue. If the supplied schema also accepts "", the
empty-value rule takes precedence and still returns undefined.
Both "" and undefined short-circuit defaults declared inside schema.
Apply a default or fallback around the returned field when absent input should
produce a value.