<a href="https://react-hook-form.com" title="React Hook Form - Simple React forms validation">
<img src="https://raw.githubusercontent.com/bluebill1049/react-hook-form/master/docs/logo.png" alt="React Hook Form Logo - React hook custom hook for form validation" />
</a>
Performant, flexible and extensible forms with easy to use validation.
This function allows you to use any external validation library such as Yup, Zod, Joi, Vest, Ajv and many others. The goal is to make sure you can seamlessly integrate whichever validation library you prefer. If you're not using a library, you can always write your own logic to validate your forms.
Install your preferred validation library alongside @hookform/resolvers.
npm install @hookform/resolvers # npm
yarn add @hookform/resolvers # yarn
pnpm install @hookform/resolvers # pnpm
bun install @hookform/resolvers # bun
Resolver Comparison
| resolver | Infer values
from schema | criteriaMode |
|---|---|---|
| AJV | ❌ | firstError \| all |
| ata-validator | ❌ | firstError \| all |
| Arktype | ✅ | firstError |
| class-validator | ✅ | firstError \| all |
| computed-types | ✅ | firstError |
| Effect | ✅ | firstError \| all |
| fluentvalidation-ts | ❌ | firstError |
| io-ts | ✅ | firstError |
| joi | ❌ | firstError \| all |
| Nope | ❌ | firstError |
| Standard Schema | ✅ | firstError \| all |
| Superstruct | ✅ | firstError |
| typanion | ✅ | firstError |
| typebox | ✅ | firstError \| all |
| typeschema | ❌ | firstError \| all |
| valibot | ✅ | firstError \| all |
| vest | ❌ | firstError \| all |
| vine | ✅ | firstError \| all |
| yup | ✅ | firstError \| all |
| zod | ✅ | firstError \| all |
Most of the resolvers can infer the output type from the schema. See comparison table for more details.
useForm<Input, Context, Output>()
Example:
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod'; // or 'zod/v4'
const schema = z.object({
id: z.number(),
});
// Automatically infers the output type from the schema
useForm({
resolver: zodResolver(schema),
});
// Force the output type
useForm<z.input<typeof schema>, any, z.output<typeof schema>>({
resolver: zodResolver(schema),
});
type Options = {
mode: 'async' | 'sync',
raw?: boolean
}
resolver(schema: object, schemaOptions?: object, resolverOptions: Options)
| type | Required | Description | |
|---|---|---|---|
| schema | object |
✓ | validation schema |
| schemaOptions | object |
validation library schema options | |
| resolverOptions | object |
resolver options, async is the default mode |
Dead simple Object schema validation.
⚠️ Pass context via
useForm({ context }), not viayupResolver'sschemaOptions.schemaOptions.contextis overridden by the form context, so use theuseFormcontext object instead.
```typescript jsx // Correct useForm({ resolver: yupResolver(schema), context: { foo: true }, });
// Avoid - schemaOptions.context will be ignored/overridden yupResolver(schema, { context: { foo: true } });
```typescript jsx
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
const schema = yup
.object()
.shape({
name: yup.string().required(),
age: yup.number().required(),
})
.required();
const App = () => {
const { register, handleSubmit } = useForm({
resolver: yupResolver(schema),
});
return (
<form onSubmit={handleSubmit((d) => console.log(d))}>
<input {...register('name')} />
<input type="number" {...register('age')} />
<input type="submit" />
</form>
);
};
TypeScript-first schema validation with static type inference
⚠️ Example below uses the
valueAsNumber, which requiresreact-hook-formv6.12.0 (released Nov 28, 2020) or later.
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod'; // or 'zod/v4'
const schema = z.object({
name: z.string().min(1, { message: 'Required' }),
age: z.number().min(10),
});
const App = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: zodResolver(schema),
});
return (
<form onSubmit={handleSubmit((d) => console.log(d))}>
<input {...register('name')} />
{errors.name?.message &&
{errors.name?.message}
}
<input type="number" {...register('age', { valueAsNumber: true })} />
{errors.age?.message &&
{errors.age?.message}
}
<input type="submit" />
</form>
);
};
A simple and composable way to validate data in JavaScript (or TypeScript).
```typescript jsx import { useForm } from 'react-hook-form'; import { superstructResolver } from '@hookform/resolvers/superstruct'; import { object, string, number } from 'superstruct';
const schema = object({ name: string(), age: number(), });
const App = () => { const { register, handleSubmit } = useForm({ resolver: superstructResolver(schema), });
return (
console.log(d))}> ); };
### [Joi](https://github.com/sideway/joi)
The most powerful data validation library for JS.
[](https://bundlephobia.com/result?p=joi)
```typescript jsx
import { useForm } from 'react-hook-form';
import { joiResolver } from '@hookform/resolvers/joi';
import Joi from 'joi';
const schema = Joi.object({
name: Joi.string().required(),
age: Joi.number().required(),
});
const App = () => {
const { register, handleSubmit } = useForm({
resolver: joiResolver(schema),
});
return (
<form onSubmit={handleSubmit((d) => console.log(d))}>
<input {...register('name')} />
<input type="number" {...register('age')} />
<input type="submit" />
</form>
);
};
Vest 🦺 Declarative Validation Testing.
```typescript jsx import { useForm } from 'react-hook-form'; import { vestResolver } from '@hookform/resolvers/vest'; import { create, test, enforce } from 'vest';
const validationSuite = create((data = {}) => { test('username', 'Username is required', () => { enforce(data.username).isNotEmpty(); });
test('password', 'Password is required', () => { enforce(data.password).isNotEmpty(); }); });
const App = () => { const { register, handleSubmit, errors } = useForm({ resolver: vestResolver(validationSuite), });
return (
console.log(data))}> ); };
### [Class Validator](https://github.com/typestack/class-validator)
Decorator-based property validation for classes.
[](https://bundlephobia.com/result?p=class-validator)
> ⚠️ Remember to add these options to your `tsconfig.json`!
"strictPropertyInitialization": false, "experimentalDecorators": true
```tsx
import { useForm } from 'react-hook-form';
import { classValidatorResolver } from '@hookform/resolvers/class-validator';
import { Length, Min, IsEmail } from 'class-validator';
class User {
@Length(2, 30)
username: string;
@IsEmail()
email: string;
}
const resolver = classValidatorResolver(User);
const App = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<User>({ resolver });
return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<input type="text" {...register('username')} />
{errors.username && <span>{errors.username.message}</span>}
<input type="text" {...register('email')} />
{errors.email && <span>{errors.email.message}</span>}
<input type="submit" value="Submit" />
</form>
);
};
Validate your data with powerful decoders.
```typescript jsx import React from 'react'; import { useForm } from 'react-hook-form'; import { ioTsResolver } from '@hookform/resolvers/io-ts'; import t from 'io-ts'; // you don't have to use io-ts-types, but it's very useful import tt from 'io-ts-types';
const schema = t.type({ username: t.string, age: tt.NumberFromString, });
const App = () => { const { register, handleSubmit } = useForm({ resolver: ioTsResolver(schema), });
return (
console.log(d))}> ); };export default App;
### [Nope](https://github.com/bvego/nope-validator)
A small, simple, and fast JS validator
[](https://bundlephobia.com/result?p=nope-validator)
```typescript jsx
import { useForm } from 'react-hook-form';
import { nopeResolver } from '@hookform/resolvers/nope';
import Nope from 'nope-validator';
const schema = Nope.object().shape({
name: Nope.string().required(),
age: Nope.number().required(),
});
const App = () => {
const { register, handleSubmit } = useForm({
resolver: nopeResolver(schema),
});
return (
<form onSubmit={handleSubmit((d) => console.log(d))}>
<input {...register('name')} />
<input type="number" {...register('age')} />
<input type="submit" />
</form>
);
};
TypeScript-first schema validation with static type inference
import { useForm } from 'react-hook-form';
import { computedTypesResolver } from '@hookform/resolvers/computed-types';
import Schema, { number, string } from 'computed-types';
const schema = Schema({
username: string.min(1).error('username field is required'),
age: number,
});
const App = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: computedTypesResolver(schema),
});
return (
<form onSubmit={handleSubmit((d) => console.log(d))}>
<input {...register('name')} />
{errors.name?.message &&
{errors.name?.message}
}
<input type="number" {...register('age', { valueAsNumber: true })} />
{errors.age?.message &&
{errors.age?.message}
}
<input type="submit" />
</form>
);
};
Static and runtime type assertion library with no dependencies
```tsx import { useForm } from 'react-hook-form'; import { typanion
$ claude mcp add resolvers \
-- python -m otcore.mcp_server <graph>