Type-Safe Schemas, Both Runtime & TS
Zod + Vue
Zod schemas double as runtime validators and TypeScript types. Combine them with VeeValidate or a small custom hook for fully typed forms.
What you'll learn
- Define a Zod schema
- Derive the TS type with z.infer
- Wire it into vee-validate with the zod adapter
Zod is a TypeScript-first schema library. Define your form shape once and you get runtime validation, parsing, and a static type — all derived from the same source.
Define a Schema
import { z } from 'zod'
export const SignupSchema = z.object({
email: z.string().email('Enter a valid email'),
password: z.string().min(8, 'Min 8 characters'),
age: z.coerce.number().int().min(18, 'Must be 18+'),
newsletter: z.boolean().default(false),
})
export type SignupForm = z.infer<typeof SignupSchema> z.infer extracts the shape so SignupForm is { email: string; password: string; age: number; newsletter: boolean } — no duplication.
Validating Manually
Pass user input through .safeParse and inspect the result:
const result = SignupSchema.safeParse(formValues)
if (!result.success) {
// result.error.flatten().fieldErrors → { email: ['...'], ... }
} else {
// result.data is fully typed and parsed
await api.signup(result.data)
} With VeeValidate
Install the Zod adapter to feed schemas into useForm:
npm install @vee-validate/zod <script setup lang="ts">
import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
import { SignupSchema } from './schema'
const { defineField, handleSubmit, errors } = useForm({
validationSchema: toTypedSchema(SignupSchema),
})
const [email] = defineField('email')
const [password] = defineField('password')
const onSubmit = handleSubmit(values => {
// values is typed as SignupForm
console.log(values)
})
</script> Refinements and Cross-Field Rules
Zod’s .refine adds custom rules — handy for “passwords match” checks:
const Schema = z.object({
password: z.string().min(8),
confirm: z.string(),
}).refine(d => d.password === d.confirm, {
message: 'Passwords do not match',
path: ['confirm'],
})