Form schema validation
use-form schema based validation with zod, yup, joi and superstruct
Docs
Package
Schema based validation
@mantine/form
supports schema validation with:
You need to install one of the libraries yourself, @mantine/form
package does not depend on any of them.
If you do not know what schema validation library to choose, use zod,
it is the most modern and developer-friendly library.
zod
Installation:
yarn add zod mantine-form-zod-resolver
Basic fields validation:
import { zodResolver } from 'mantine-form-zod-resolver';
import { z } from 'zod';
import { useForm } from '@mantine/form';
const schema = z.object({
name: z
.string()
.min(2, { message: 'Name should have at least 2 letters' }),
email: z.string().email({ message: 'Invalid email' }),
age: z.number().min(18, {
message: 'You must be at least 18 to create an account',
}),
});
const form = useForm({
mode: 'uncontrolled',
initialValues: {
name: '',
email: '',
age: 16,
},
validate: zodResolver(schema),
});
form.validate();
form.errors;
// -> {
// name: 'Name should have at least 2 letters',
// email: 'Invalid email',
// age: 'You must be at least 18 to create an account'
// }
Nested fields validation
import { zodResolver } from 'mantine-form-zod-resolver';
import { z } from 'zod';
import { useForm } from '@mantine/form';
const nestedSchema = z.object({
nested: z.object({
field: z
.string()
.min(2, { message: 'Field should have at least 2 letters' }),
}),
});
const form = useForm({
mode: 'uncontrolled',
initialValues: {
nested: {
field: '',
},
},
validate: zodResolver(nestedSchema),
});
form.validate();
form.errors;
// -> {
// 'nested.field': 'Field should have at least 2 letters',
// }
List fields validation:
import { zodResolver } from 'mantine-form-zod-resolver';
import { z } from 'zod';
import { useForm } from '@mantine/form';
const listSchema = z.object({
list: z.array(
z.object({
name: z
.string()
.min(2, { message: 'Name should have at least 2 letters' }),
})
),
});
const form = useForm({
mode: 'uncontrolled',
initialValues: {
list: [{ name: '' }],
},
validate: zodResolver(listSchema),
});
form.validate();
form.errors;
// -> {
// 'list.0.name': 'Name should have at least 2 letters',
// }
yup
Installation:
yarn add yup mantine-form-yup-resolver
Basic fields validation:
import { yupResolver } from 'mantine-form-yup-resolver';
import * as yup from 'yup';
import { useForm } from '@mantine/form';
const schema = yup.object().shape({
name: yup.string().min(2, 'Name should have at least 2 letters'),
email: yup
.string()
.required('Invalid email')
.email('Invalid email'),
age: yup
.number()
.min(18, 'You must be at least 18 to create an account'),
});
const form = useForm({
mode: 'uncontrolled',
initialValues: {
name: '',
email: '',
age: 16,
},
validate: yupResolver(schema),
});
form.validate();
form.errors;
// -> {
// name: 'Name should have at least 2 letters',
// email: 'Invalid email',
// age: 'You must be at least 18 to create an account'
// }
Nested fields validation:
import { yupResolver } from 'mantine-form-yup-resolver';
import * as yup from 'yup';
import { useForm } from '@mantine/form';
const nestedSchema = yup.object().shape({
nested: yup.object().shape({
field: yup
.string()
.min(2, 'Field should have at least 2 letters'),
}),
});
const form = useForm({
mode: 'uncontrolled',
initialValues: {
nested: {
field: '',
},
},
validate: yupResolver(nestedSchema),
});
form.validate();
form.errors;
// -> {
// 'nested.field': 'Field should have at least 2 letters',
// }
List fields validation:
import { yupResolver } from 'mantine-form-yup-resolver';
import * as yup from 'yup';
import { useForm } from '@mantine/form';
const listSchema = yup.object().shape({
list: yup.array().of(
yup.object().shape({
name: yup
.string()
.min(2, 'Name should have at least 2 letters'),
})
),
});
const form = useForm({
mode: 'uncontrolled',
initialValues: {
list: [{ name: '' }],
},
validate: yupResolver(listSchema),
});
form.validate();
form.errors;
// -> {
// 'list.0.name': 'Name should have at least 2 letters',
// }
joi
Installation:
yarn add joi mantine-form-joi-resolver
Basic fields validation:
import Joi from 'joi';
import { joiResolver } from 'mantine-form-joi-resolver';
import { useForm } from '@mantine/form';
const schema = Joi.object({
name: Joi.string().min(2).messages({
'string.min': 'Name should have at least 2 letters',
'string.empty': 'Name should have at least 2 letters',
}),
email: Joi.string()
.email({ tlds: { allow: false } })
.messages({
'string.email': 'Invalid email',
'string.empty': 'Invalid email',
}),
age: Joi.number()
.min(18)
.message('You must be at least 18 to create an account'),
});
const form = useForm({
mode: 'uncontrolled',
initialValues: {
name: '',
email: '',
age: 16,
},
validate: joiResolver(schema),
});
form.validate();
form.errors;
// -> {
// name: 'Name should have at least 2 letters',
// email: 'Invalid email',
// age: 'You must be at least 18 to create an account'
// }
Nested fields validation:
import Joi from 'joi';
import { joiResolver } from 'mantine-form-joi-resolver';
import { useForm } from '@mantine/form';
const nestedSchema = Joi.object({
nested: Joi.object({
field: Joi.string().min(2).messages({
'string.min': 'Field should have at least 2 letters',
'string.empty': 'Field should have at least 2 letters',
}),
}),
});
const form = useForm({
mode: 'uncontrolled',
initialValues: {
nested: {
field: '',
},
},
validate: joiResolver(nestedSchema),
});
form.validate();
form.errors;
// -> {
// 'nested.field': 'Field should have at least 2 letters',
// }
List fields validation:
import Joi from 'joi';
import { joiResolver } from 'mantine-form-joi-resolver';
import { useForm } from '@mantine/form';
const listSchema = Joi.object({
list: Joi.array().items(
Joi.object({
name: Joi.string().min(2).messages({
'string.min': 'Name should have at least 2 letters',
'string.empty': 'Name should have at least 2 letters',
}),
})
),
});
const form = useForm({
mode: 'uncontrolled',
initialValues: {
list: [{ name: '' }],
},
validate: joiResolver(listSchema),
});
form.validate();
form.errors;
// -> {
// 'list.0.name': 'Name should have at least 2 letters',
// }
superstruct
Installation:
yarn add superstruct mantine-form-superstruct-resolver
Basic fields validation:
import isEmail from 'is-email';
import { superstructResolver } from 'mantine-form-superstruct-resolver';
import * as s from 'superstruct';
const emailString = s.define('email', isEmail);
const schema = s.object({
name: s.size(s.string(), 2, 30),
email: emailString,
age: s.min(s.number(), 18),
});
const form = useForm({
mode: 'uncontrolled',
initialValues: {
name: '',
email: '',
age: 16,
},
validate: superstructResolver(schema),
});
form.validate();
form.errors;
// -> {
// name: 'name: Expected a string with a length between `2` and `30` but received one with a length of `0`',
// email: 'email: Expected a value of type `email`, but received: `""`',
// age: 'age: Expected a number greater than or equal to 18 but received `16`',
// }
Nested fields validation:
import { superstructResolver } from 'mantine-form-superstruct-resolver';
import * as s from 'superstruct';
import { useForm } from '@mantine/form';
const nestedSchema = s.object({
nested: s.object({
field: s.size(s.string(), 2, 30),
}),
});
const form = useForm({
mode: 'uncontrolled',
initialValues: {
nested: {
field: '',
},
},
validate: superstructResolver(nestedSchema),
});
form.validate();
form.errors;
// -> {
// 'nested.field': 'nested field: Expected a string with a length between `2` and `30` but received one with a length of `0`',
// }
List fields validation:
import { superstructResolver } from 'mantine-form-superstruct-resolver';
import * as s from 'superstruct';
import { useForm } from '@mantine/form';
const listSchema = s.object({
list: s.array(
s.object({
name: s.size(s.string(), 2, 30),
})
),
});
const form = useForm({
mode: 'uncontrolled',
initialValues: {
list: [{ name: '' }],
},
validate: superstructResolver(listSchema),
});
form.validate();
form.errors;
// -> {
// 'list 0 name: Expected a string with a length between `2` and `30` but received one with a length of `0`',
// }
valibot
Installation:
yarn add valibot mantine-form-valibot-resolver
Basic fields validation:
import { valibotResolver } from 'mantine-form-valibot-resolver';
import * as v from 'valibot';
import { useForm } from '@mantine/form';
const schema = v.object({
name: v.pipe(
v.string(),
v.minLength(2, 'Name should have at least 2 letters')
),
email: v.pipe(v.string(), v.email('Invalid email')),
age: v.pipe(
v.number(),
v.minValue(18, 'You must be at least 18 to create an account')
),
});
const form = useForm({
initialValues: {
name: '',
email: '',
age: 16,
},
validate: valibotResolver(schema),
});
form.validate();
form.errors;
// -> {
// name: 'Name should have at least 2 letters',
// email: 'Invalid email',
// age: 'You must be at least 18 to create an account'
// }
Nested fields validation:
import { valibotResolver } from 'mantine-form-valibot-resolver';
import * as v from 'valibot';
import { useForm } from '@mantine/form';
const nestedSchema = v.object({
nested: v.object({
field: v.pipe(
v.string(),
v.minLength(2, 'Field should have at least 2 letters')
),
}),
});
const form = useForm({
initialValues: {
nested: {
field: '',
},
},
validate: valibotResolver(nestedSchema),
});
form.validate();
form.errors;
// -> {
// 'nested.field': 'Field should have at least 2 letters',
// }
List fields validation:
import { valibotResolver } from 'mantine-form-valibot-resolver';
import * as v from 'valibot';
import { useForm } from '@mantine/form';
const listSchema = v.object({
list: v.array(
v.object({
name: v.pipe(
v.string(),
v.minLength(2, 'Name should have at least 2 letters')
),
})
),
});
const form = useForm({
initialValues: {
list: [{ name: '' }],
},
validate: valibotResolver(listSchema),
});
form.validate();
form.errors;
// -> {
// 'list.0.name': 'Name should have at least 2 letters',
// }
With TypeScript:
You can use the InferInput
type from the valibot
library to get the type of the form data.
import { valibotResolver } from 'mantine-form-valibot-resolver';
import * as v from 'valibot';
import { useForm } from '@mantine/form';
export const userSchema = v.object({
email: v.pipe(v.string(), v.email()),
});
type FormData = v.InferInput<typeof userSchema>;
const form = useForm<FormData>({
initialValues: {
email: '',
},
validate: valibotResolver(userSchema),
});
Build fully functional accessible web applications faster than ever