What is Zod?
According to their introduction, Zod is TypeScript-first schema validation with static type inference library. Zod is designed to be as developer-friendly as possible.
Install
npm install zod # npm
yarn add zod # yarn
pnpm add zod # pnpm
Install library in your project. It also supports bun and Deno.
tsconfig.json
{
"compilerOptions": {
"strict": true
}
}
Your Typescript version should over 4.5+. You also must enable strict
in your tsconfig.json
.
The requirements are written in Documentation.
What is VeeValidate
VeeValidate is form validator library for Vue. It's one of popular library in Vue. It takes care of value tracking, validation, errors, submissions and more
install
npm i vee-validate --save # npm
yarn add vee-validate # yarn
pnpm add vee-validate # pnpm
You can use it with a script tag and a CDN.
<script src="https://unpkg.com/vee-validate" />
It will inject VeeValidate
global Object once you import library.
How to mix two libraries.
Zod will be used for form rules. On the other hands, Vee-validator will be used for submissions.
Let's create a registration form. The example will be written <script setup>
.
The example code will have three input fields, email, password and confirm password.
Install
Install
npm install @vee-validate/zod # npm
yarn add @vee-validate/zod # yarn
pnpm add @vee-validate/zod # pnpm
Install official vee-validate plugins which allows you to use zod schemas.
Create schema
const validationSchema = toFormValidator(
z.object({
email: z.string({
required_error: 'Type email'
})
.email({
message: 'Type email',
})
.max(30, {
message: 'Max Email '
})
.trim().min(1, {
message: 'Type email'
}),
password: z.string({
required_error: 'Type password'
})
.refine(val => {
// Not working....
// Check length
if (val.length < 8) return false
// Check lowercase
if (val.search(/[a-z]/i) < 0) return false
// Check uppercase
if (val.search(/[A-Z]/i) < 0) return false
// Check number
if (val.search(/[0-9]/) < 0) return false
// Check special characters
if (val.search( /^(?=.*[~`!@#$%^&*()--+={}\[\]|\\:;"'<>,.?/_₹]).*$/) < 0) return false
return true
}, {
message: 'Password must include at least one lower letter, one uppercase letter, and special letter',
}),
confirmPassword: z.string({
required_error: 'Type password'
}).refine(val => val === password.value, {
message: 'Password is not matched'
})
})
)
toFormValidator
generates validation schema along with Zod
create form
const { handleSubmit, errors, isSubmitting, resetForm } = useForm({
validationSchema,
})
useForm
: Creates a vee-validate’s form context and associates any fields inside the same component or its children with it automatically, which you will use to create custom form components and to manage your fields in general.
handleSubmit
function is used for executing your callback once the returned function like onSubmit
.
erros
is object which contains error messages. If field doesn't have any errors, errors.field will return undefined
isSubmitting
indicates the form whether form is submitting or not.
resetForm
function will reset your all fields.
<Form :validation-schema="schema">
...
</Form>
If you would like to use <Form />
, add it in HTML
section. However, the example will not care of it.
make some fields
const { value: email } = useField('email')
const { value: password } = useField('password')
const { value: confirmPassword } = useField('confirmPassword')
submit function
const onSubmit = handleSubmit((values) => {
console.log(values)
resetForm()
})
Once you hit the submit button, this function will be called if all rules are passed in Zod schema. parameter "values" include all values of fields.
HTML
<template>
<form @submit.prevent="onSubmit">
<label
class="label"
for="email"
>
<span>email</span>
</label>
<input
id="email"
v-model="email"
placeholder="type email"
type="email"
>
<p v-if="errors.email">
<span>
{{ errors.email }}
</span>
</p>
<label
class="label"
for="password"
>
<span>password</span>
</label>
<input
id="password"
v-model="password"
placeholder="type password"
type="password"
>
<p v-if="errors.password">
<span>
{{ errors.password }}
</span>
</p>
<label
class="label"
for="confirmPassword"
>
<span>confirm password</span>
</label>
<input
id="confirmPassword"
v-model="confirmPassword"
placeholder="type password"
type="password"
>
<p v-if="errors.confirmPassword">
<span>
{{ errors.confirmPassword }}
</span>
</p>
<button
class="btn btn-primary w-full"
type="submit"
>
register
</button>
</form>
</template>
Create submit button which has "submit" as a type. You also have to add submit events in your form.
Full Source code
<script setup lang="ts">
import { toTypedSchema } from '@vee-validate/zod'
import { z } from 'zod'
import { useField, useForm } from 'vee-validate'
const validationSchema = toTypedSchema(
z.object({
email: z.string({
required_error: 'Type email'
})
.email({
message: 'Type email',
})
.max(30, {
message: 'Max Email '
})
.trim().min(1, {
message: 'Type email'
}),
password: z.string({
required_error: 'Type pasword'
}).refine(val => {
// Not working....
// Check length
if (val.length < 8) return false
// Check lowercase
if (val.search(/[a-z]/i) < 0) return false
// Check uppercase
if (val.search(/[A-Z]/i) < 0) return false
// Check number
if (val.search(/[0-9]/) < 0) return false
// Check special characters
if (val.search( /^(?=.*[~`!@#$%^&*()--+={}\[\]|\\:;"'<>,.?/_₹]).*$/) < 0) return false
return true
}, {
message: 'Password must include at least one lower letter, one uppercase letter, and special letter',
}),
confirmPassword: z.string({
required_error: 'Type password'
}).refine(val => val === password.value, {
message: 'Password is not matched'
})
})
)
const { handleSubmit, errors, isSubmitting, resetForm } = useForm({
validationSchema,
})
const { value: email } = useField('email')
const { value: password } = useField('password')
const { value: confirmPassword } = useField('confirmPassword')
const onSubmit = handleSubmit((values) => {
console.log(values)
resetForm()
})
</script>
<template>
<form @submit.prevent="onSubmit">
<label
class="label"
for="email"
>
<span>email</span>
</label>
<input
id="email"
v-model="email"
placeholder="type email"
type="email"
>
<p v-if="errors.email">
<span>
{{ errors.email }}
</span>
</p>
<label
class="label"
for="password"
>
<span>password</span>
</label>
<input
id="password"
v-model="password"
placeholder="type password"
type="password"
>
<p v-if="errors.password">
<span>
{{ errors.password }}
</span>
</p>
<label
class="label"
for="confirmPassword"
>
<span>confirm password</span>
</label>
<input
id="confirmPassword"
v-model="confirmPassword"
placeholder="type password"
type="password"
>
<p v-if="errors.confirmPassword">
<span>
{{ errors.confirmPassword }}
</span>
</p>
<button
class="w-full"
type="submit"
>
register
</button>
</form>
</template>