Choosing Between Yup and Zod for VeeValidate 4 in Vue 3 Projects
Guide for selecting Yup or Zod as the validation schema provider in VeeValidate 4 for Vue 3, with a comparison table, trade‑off analysis, and a ready‑to‑run Zod example.
09 May 2026, 12:45 UTC

Decision and Constraints
When building a Vue 3 application with VeeValidate 4.x you must pick a schema validation library that works with the validation-schema prop. The choice influences bundle size, TypeScript developer experience, and how easy it is to migrate existing Yup schemas. The decision should be guided by:
- Maximum allowed bundle increase for the validation library.
- Need for precise TypeScript inference of form values.
- Team familiarity with imperative vs. declarative schema APIs.
- Plans for async validation that may need abort signals or multiple error messages per field.
Comparison Table
| Criterion | Yup | Zod |
|---|---|---|
| Bundle size (min+gzip) | ~12.5 KB | ~8.5 KB |
| TypeScript inference | yup.InferType<typeof schema> (historically weaker for nested/tuple types) |
z.infer<typeof schema> (exact shape, including nested objects and tuples) |
| API style | Imperative chainable: string().email().required() |
Declarative object‑style: z.object({ email: z.string().email() }) |
| Async validation | .test() (single message per field) |
.refine() / .superRefine() (multiple issues, abortSignal support) |
| Error message localisation | Global setLocale (side‑effectful) |
Per‑schema errorMap (isolated) |
| Maturity & ecosystem | Very mature, many third‑party plugins | Rapidly growing, strong TS focus, fewer community extensions |
Trade‑offs
If bundle size is a hard constraint (e.g., a library that must stay under 50 KB total), Zod’s smaller footprint gives you a margin. If you rely heavily on Yup’s conditional when() blocks or custom locales that are already set globally, migrating may introduce regression risk unless you refactor those pieces to Zod’s superRefine or per‑schema error maps.
For teams that prioritise end‑to‑end type safety—where the values returned by useForm should match the schema exactly without extra type gymnastics—Zod’s inference eliminates the need for wrapper types and reduces the chance of mismatched form models.
When the project already has a large Yup‑based validation layer and the team is comfortable with its imperative API, staying with Yup avoids a costly rewrite. In that case, you can still benefit from VeeValidate’s integration while accepting the slightly larger bundle and less precise TS inference.
Concrete Implementation: Zod with VeeValidate 4
The following example shows how to set up a login form using Zod, verify the inferred TypeScript type, and handle async server‑side validation.
- Install the required packages in your Vue 3 + Vite project:
# Run in the project root (requires npm or yarn access)
npm i vee-validate@4 zod@3 @vee-validate/zod
# or: yarn add vee-validate@4 zod@3 @vee-validate/zod
- Create a Zod schema for the login form:
// src/schemas/loginSchema.ts
import { z } from 'zod';
export const loginSchema = z.object({
email: z.string().email('Invalid email address'),
password: z.string().min(8, 'Password must be at least 8 characters'),
// async check that the email exists in the backend
// superRefine lets us push multiple issues and respect abortSignal
}).superRefine((value, ctx) => {
if (!value.email || !value.password) return; // skip if basic rules fail
// Example async call – replace with your actual API
return fetch(`/api/auth/check-email?email=${encodeURIComponent(value.email)}`, {
signal: ctx.abortSignal,
})
.then(resp => {
if (!resp.ok) {
ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Email not found', path: ['email'] });
}
});
});
- Use the schema in a VeeValidate form:
// src/components/LoginForm.vue
Email:
Password:
Log In
After saving the file, run your IDE’s type‑check (e.g., vue-tsc --noEmit) and hover over values inside handleSubmit. You should see the inferred type reflect the schema fields exactly, with no extra InferType wrapper.
Verification Steps
- Bundle size check: run
npm run buildthen inspect the output withrollup-plugin-visualizerorsource-map-explorer. Verify that the chunk containing Zod is around 8–9 KB gzipped. - Type inference check: In VS Code, place the cursor on
valuesin thehandleSubmitfunction. The tooltip should show{ email: string; password: string }(or whatever fields your schema defines). - Async validation check: Disable the mock API or point to a non‑existent endpoint. Submit the form with a valid email format; the
ErrorMessagefor the email field should display “Email not found” after the request resolves. - Migration parity test: Take an existing Yup schema (e.g., a nested object with
when) and rewrite it using Zod equivalents. Run your unit test suite that asserts validation errors; the pass/fail results should match before and after the change.
Limitations and Practical Checks
Even though Zod offers better TS inference, the VeeValidate Zod adapter (@vee-validate/zod) adds a thin layer that may not preserve every Zod feature (e.g., custom errorMap must be passed via the validationSchema option, not directly on the Zod schema). Verify that any custom error map you define is respected by checking the rendered ErrorMessage content.
If you rely on Yup’s global locale (setLocale) for messages across many forms, switching to Zod will require moving those definitions into each schema’s errorMap or providing a shared helper function. Test that messages appear in the correct language after the change.
Valibot is mentioned as a smaller alternative, but its ecosystem is less mature; if you consider it, run the same bundle‑size and type‑inference checks to confirm that the savings outweigh the missing refinements.
When to Stick with Yup
- Your project already has a large, well‑tested Yup validation library and the migration effort would exceed the bundle‑size gain.
- You depend on Yup’s
when()for complex conditional logic that would become hard to read with Zod’ssuperRefine. - Your team is more comfortable with the imperative chainable style and prefers to keep a single source of truth for error messages via
setLocale.
Conclusion
Choosing between Yup and Zod for VeeValidate 4 in Vue 3 hinges on three practical factors: bundle budget, TypeScript precision, and API familiarity. Use the table above to weigh those factors, then follow the concrete Zod implementation to validate that the inferred types and async behavior meet your expectations. If the constraints favor minimal disruption and existing Yup investment, retain Yup but monitor bundle growth. Either path, verify the decision with the outlined checks before merging to main.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.