diff --git a/docs/src/pages/guide/composition-api/api-review.mdx b/docs/src/pages/guide/composition-api/api-review.mdx
deleted file mode 100644
index ce69cae6b..000000000
--- a/docs/src/pages/guide/composition-api/api-review.mdx
+++ /dev/null
@@ -1,69 +0,0 @@
----
-layout: ../../../layouts/PageLayout.astro
-title: Composition API Review
-description: Using `useField` and `useForm` to compose validation into your inputs
-order: 6
-next: guide/composition-api/caveats
-menuTitle: Review
----
-
-# Composition API Review
-
-Here the difference between the components API and the composition API ends, both APIs work the same for the global validators and `i18n` support. This page will quickly review what you learned about the composition API and offer some tips.
-
-vee-validate is built from the ground up with the composition API through a collection of functions, mainly the `useField` and `useForm` functions. Internally the `
` and `
` components actually use the composition functions under the hood.
-
-Meaning you can create your own custom input and form components and they will be treated the same as `
` and `
` components. You can mix them together and use a `Form` component with any other custom component that uses `useField` and vice versa.
-
-Aside from `useField` and `useForm`, vee-validate offers simpler utility composable functions that you can use to build very specific and specialized components that contribute to your form experience, they are mentioned later on in this page.
-
-## When to use composition API
-
-Before you go through the details of `useField` and `useForm`, you first need to understand when you should be using the composition functions over the components.
-
-vee-validate **does not** deal with arbitrary values, meaning you should not (while you could) use the composition API as means to force vee-validate to validate some objects/data. To validate arbitrary values, you can use other libraries or validation tools, like `yup` or `validator.js` which you are likely to use anyways.
-
-To re-iterate, vee-validate is all about forms and inputs. Not values, so if you need a piece of data validated, you need to express it as a form field.
-
-Now that we covered what vee-validate is about, **when should you use the composition API over the declarative components?**
-
-The declarative components cover tons of use-cases and they are extremely easy to use, for most forms you should use them. They are especially useful if you are working with native HTML input elements, or generally do not have heavy UI customization.
-
-When your input's complexities grow, you might then consider using `useField` and `useForm`. While they require more labor to integrate into your inputs, they give you access to the same logical parts of vee-validate's validation at a much lower level. They are great if you are building your own UI library/framework with form controls.
-
-It's up to you what to do with what `useField` and `useForm` give you, for example, `useField` doesn't handle input events automatically, you will need to do that.
-
-To summarize, **the validation composition API is a great power with great responsibility.**
-
-## Composition Helpers
-
-Aside from `useField` and `useForm`, these are a collection of simple functions that you can use to opt-in specific parts of vee-validate features like form state and various actions you can perform on fields and forms.
-
-Here are a few examples of what you can build with these functions:
-
-- A custom submission progress component
-- A custom error message component.
-- A form validity indicators
-- reset buttons or submit buttons
-
-Here is a list of the functions available that you can use:
-
-- `useFieldError` Gives access to a single field's first error message
-- `useFormErrors` Gives access to the entire error bag of the form
-- `useIsFieldDirty` If a field is dirty
-- `useIsFormDirty` If the form is dirty (form contains at least one dirty field)
-- `useIsFieldTouched` If a field is touched
-- `useIsFormTouched` If the form is touched (form contains at least one touched field)
-- `useIsFieldValid` If a field is valid
-- `useIsFormValid` If all fields are **validated and valid**
-- `useValidateField` Returns a function that validates a specific field
-- `useValidateForm` Returns a function that validates the entire form
-- `useResetForm` Resets the form to its initial state
-- `useSubmitForm` Creates a submission function that validates and submits the form (even if no `form` element is involved)
-- `useIsSubmitting` If the form is currently submitting
-- `useIsValidating` If the form is currently validating by validate function
-- `useSubmitCount` The number of times the user attempted to submit the form
-- `useFieldValue` Returns a specific fields' current value
-- `useFormValues` Returns the current form field values
-
-For more information about the functions, you can head over to the [API reference and check them out](/api/composition-helpers).
diff --git a/docs/src/pages/guide/composition-api/caveats.mdx b/docs/src/pages/guide/composition-api/caveats.mdx
index 4cb65eab4..6bee3684e 100644
--- a/docs/src/pages/guide/composition-api/caveats.mdx
+++ b/docs/src/pages/guide/composition-api/caveats.mdx
@@ -1,11 +1,9 @@
---
layout: ../../../layouts/PageLayout.astro
-title: Composition API Caveats
+title: Caveats and best practices
description: Things to watch out for when using the composition API
order: 7
-next: guide/global-validators
-menuTitle: Caveats
-new: true
+menuTitle: Best practices
---
# Composition API Caveats
diff --git a/docs/src/pages/guide/composition-api/custom-inputs.mdx b/docs/src/pages/guide/composition-api/custom-inputs.mdx
new file mode 100644
index 000000000..45c0b173d
--- /dev/null
+++ b/docs/src/pages/guide/composition-api/custom-inputs.mdx
@@ -0,0 +1,396 @@
+---
+layout: ../../../layouts/PageLayout.astro
+title: Custom inputs
+description: Field-level and form-level validation and validation behavior and error messages with composition API
+order: 4
+next:
+ path: guide/composition-api/helpers
+ title: Composable Helpers
+ description: Access any field or form state within child components with these composable helpers.
+ intro: |
+ You've learned to build your form, your custom inputs. Now it is a quick one to figure out how to build special components like submission buttons, reset buttons, error displays and more with these composable helpers.
+---
+
+# Building Custom inputs
+
+import DocTip from '@/components/DocTip.vue';
+import Repl from '@/components/MdxRepl.vue';
+
+## Imperative vs Declarative
+
+So far we've been using `useForm` to create forms and use `defineInputBinds` or `defineComponentBinds` to create our field binding objects to integrate them with our fields. However that usually requires a lot of boilerplate code to create the binding object and the field component.
+
+For example this how a 5-field form looks like with `useForm` and `defineInputBinds`:
+
+```js
+const { defineInputBinds } = useForm();
+
+const email = defineInputBinds('email');
+const firstName = defineInputBinds('firstName');
+const lastName = defineInputBinds('lastName');
+const password = defineInputBinds('password');
+const passwordConfirm = defineInputBinds('passwordConfirm');
+```
+
+This can get ugly very quickly especially if you have a bunch of field arrays or nested fields involved. This is one of the downsides of using an imperative API, but with `useField` you can switch to declarative API and get rid of all that boilerplate code.
+
+`useField` is a composition function that similar to how `useForm` makes it easier to manage forms, it makes easier to create and manage input components. You should use it when creating custom input components and that means you've made the choice that vee-validate will be an integral part of your input component system.
+
+## Creating a custom input component
+
+Let's start with a simple example, we will create a `InputText` component that represents a text field. It can be as simple as this:
+
+```vue
+
+
+ {{ errorMessage }}
+
+
+
+```
+
+This works exactly the same way as with `defineInputBinds`, but now since you have a vee-validate field component, you can use it directly in any component with a form context and it will just work:
+
+Here is a live example:
+
+
+
+Notice how much of the burden of defining fields went away as soon as switched to the declarative approach. This is where `useField` really shines, but that's just us getting started. Following along in this guide to make the most out of `useField`.
+
+## Validation
+
+All previous examples have used the form's validation schema to validate the individual fields, however you can also define a validation schema for each field individually. The same types of validation libraries are supported.
+
+
+
+You can only validate using field-level validation or form-level validation, you cannot mix the two approaches here.
+
+
+
+### Validating with yup
+
+You can use Yup schemas to validate fields individually by passing the schema as the second argument to `useField`.
+
+
+
+### Validating with Zod
+
+You can use [Zod](https://github.com/colinhacks/zod), however you will need to add `@vee-validate/zod` to your dependencies.
+
+```sh
+# with npm
+npm i @vee-validate/zod
+# with pnpm
+pnpm add @vee-validate/zod
+# with yarn
+yarn add @vee-validate/zod
+```
+
+Then you can wrap your zod field schemas with `toTypedSchema` function. [More on that here](/guide/composition-api/typed-schema).
+
+
+
+### Validating with global validators
+
+Another option is using `@vee-validate/rules` which have been historically bundled with past versions of vee-validate. It includes rules that can be defined globally and then used anywhere using Laravel-like string expressions.
+
+You can refer to the full guide on global rules [here](/guide/global-validators).
+
+### Validating with functions
+
+Another option is to just use any 3rd party validation tool you prefer.
+
+
+
+### Triggers
+
+#### Default Behavior
+
+By default vee-validate runs validation whenever the `value` ref changes whether it was bound by a `v-model` or changed in your code:
+
+```js
+const { value } = useField('fieldName', yup.string().required());
+
+// validation WILL be triggered
+value.value = 'something';
+```
+
+You can disable that behavior by passing a `validateOnValueUpdate` option set to `false`:
+
+```js
+const { value } = useField('fieldName', yup.string().required(), {
+ validateOnValueUpdate: false,
+});
+
+// validation WILL NOT trigger
+value.value = 'something';
+```
+
+#### Handling Events
+
+`useField()` composition function is not concerned with any events, it only validates whenever the `value` ref changes. However, it gives you everything you need to set up your own validation experience.
+
+The `useField` function exposes some handler functions, each handles a specific aspect of the validation experience:
+
+- `handleChange`: Updates the field value, can be configured to trigger validation or silently update the value
+- `handleBlur`: Updates the `meta.touched` flag, doesn't trigger validation.
+
+```js
+const { handleChange, handleBlur } = useField('someField');
+```
+
+In this example we are validating on `input` event (when the user types), which would make the validation aggressive:
+
+
+
+With a slight adjustment we can make our validation lazy by changing the listener to `@change` (validates when the user leaves the control):
+
+```vue-html
+
+
+ {{ errorMessage }}
+
+```
+
+Note that `handleChange` can be called anywhere, not just in the template and not as just a event handler. You can use it to mutate the field value whenever you want, as an added bonus you can choose if `handleChange` should trigger a validation or not.
+
+```js
+const { handleChange } = useField('someField');
+
+// validates by default
+handleChange('new value');
+// validates
+handleChange('new value', true);
+// Doesn't validate
+handleChange('new value', false);
+```
+
+Let's say you want to validate on `blur` instead. You can use the `handleBlur` in a similar way. The main differences are:
+
+- `handleBlur` doesn't mutate the `value` of the field. It only sets the `meta.touched` to `true`.
+- `handleBlur` does not validate the current value by default, you have to pass `true` as a second argument to trigger validation.
+
+With that info in mind, you can validate on `blur` like so:
+
+```vue-html
+
+
+ {{ errorMessage }}
+
+```
+
+As you can see, the `useField` doesn't care which events you use `handleChange` for. This allows for greater flexibility that's not possible with the `
` component, not as straightforward at least.
+
+Consider this validation experience:
+
+- Validate on Change/Blur initially (when user leaves the control), let's call this lazy mode.
+- If the field is invalid, switch the validation to validate on input (when user types), let's call this aggressive mode.
+- If the field is valid, go back to "lazy" mode, otherwise be "aggressive".
+
+Implementing this requires some knowledge about how the `v-on` (we can bind objects on it) handler works.
+
+```js
+const { errorMessage, value, handleChange } = useField(() => props.name, undefined, {
+ validateOnValueUpdate: false,
+});
+
+const validationListeners = {
+ blur: evt => handleBlur(evt, true),
+ change: handleChange,
+ input: evt => handleChange(evt, !!errorMessage.value),
+};
+```
+
+Then in your template you can use `v-on` to add your listener object:
+
+```vue-html
+
+```
+
+Here is a full example:
+
+
+
+## v-model Support
+
+The `useField` function can automatically manage `v-model` integration for you. Usually you will need to do this in every component you create:
+
+```js
+const props = defineProps({
+ modelValue: String,
+});
+
+const emit = defineEmits(['update:modelValue']);
+```
+
+Instead you can let `useField` do that for you by telling it to enable `v-model` syncing:
+
+```js
+const props = defineProps({
+ modelValue: String,
+});
+
+const { value, errorMessage } = useField('fieldName', undefined, {
+ syncVModel: true,
+});
+```
+
+Now whenever `value` changes, you will emit an `update:modelValue` event with the new value. This is useful when you want to use `v-model` with your custom input component:
+
+
+
+You can also use different prop names for the `modelValue`, for example `v-model:text` can be implemented by passing the model name directly to `syncVModel`.
+
+```js
+const props = defineProps({
+ text: String,
+});
+
+const { value, errorMessage } = useField('fieldName', undefined, {
+ syncVModel: 'text',
+});
+```
+
+This will emit `onUpdate:text` instead of `onUpdate:modelValue` whenever the `value` changes.
+
+## Displaying Error Messages
+
+You've already seen how to display errors with `useForm`. With `useField` you can use `errorMessage` ref:
+
+```js
+const { errorMessage, value } = useField('fieldName', yup.string().required());
+
+// contains the error message if available
+errorMessage.value;
+```
+
+In addition to this, you can get all errors for the field using the `errors` ref which contains multiple error messages if applicable:
+
+```js
+const { errors, value } = useField('fieldName', yup.string().required());
+
+// contains array of error messages, otherwise empty array
+errors.value;
+```
+
+Here is an example where each field displays its entire range of error messages:
+
+
+
+### Custom Field Labels
+
+More often than not, your fields will have names with underscores or shorthands which isn't very nice when showing in error messages, for example, you might have specific encoding to your field names because they might be generated by the backend. Ideally, you want to avoid having messages like:
+
+```txt
+The down_p is required
+```
+
+And instead show something more meaningful to the user
+
+```txt
+The down payment is required
+```
+
+You can do this in two ways depending on which validators you are using (yup or [global validators](/guide/global-validators)).
+
+#### Custom Labels with Yup
+
+With yup it is very straightforward, you just need to call `label()` after defining your field's validations either in field level or form level:
+
+```js
+const schema = Yup.object({
+ email_addr: Yup.string().email().required().label('Email Address'),
+ acc_password: Yup.string().min(5).required().label('Your Password'),
+});
+```
+
+#### Custom Labels with Zod
+
+With Zod it is similar, you call the `label` function and give it the name you want to use in the generated message.
+
+```js
+const schema = z.object({
+ email_addr: z.string().email().label('Email Address'),
+ acc_password: z.string().min(5).label('Your Password'),
+});
+```
+
+If you are interested in how to do the same for global validators check the [i18n guide](/guide/i18n#custom-labels)
+
+## Field-level Meta
+
+Each field has metadata associated with it, the `meta` property returned from `useField` contains information about the field:
+
+- `valid`: The current field validity, automatically updated for you.
+- `touched`: If the field was **touched**, can be updated with `setTouched` on `useField` return value.
+- `dirty`: If the field value was updated, you cannot change its value.
+- `pending`: If the field's validations are still running, useful for long-running async validation.
+- `initialValue`: The field's initial value, is `undefined` if you didn't specify any.
+
+```js
+const { meta } = useField('fieldName');
+
+meta.dirty;
+meta.pending;
+meta.touched;
+meta.valid;
+meta.initialValue;
+```
+
+This is the typescript interface for a field's meta value
+
+```ts
+interface FieldMeta {
+ dirty: boolean;
+ pending: boolean;
+ touched: boolean;
+ valid: boolean;
+ initialValue: any;
+}
+```
+
+Just like how the form's `meta` is readonly, this is also readonly and you cannot change it directly. Actually, only the `touched` meta value can be mutated using `handleBlur`, all other meta values are automatically updated for you as the field validates or when it changes its value.
+
+```js
+const { meta, handleBlur } = useField('fieldName');
+
+// updates meta.touched = true
+handleBlur();
+```
+
+
+
+Since the `meta.valid` flag is initially `true` (because it just means there are no errors yet), it would cause problems if you have a "success" UI state an indicator.
+
+To avoid this case you should combine the `valid` flag with either `meta.dirty` or `meta.touched` to get an accurate representation. You will see that in action in the next example.
+
+
+
+In the following example, we the various meta information flags to style the input with some styling.
+
+
+
+
+
+Notice in the previous example, we passed an `initialValue`, this is because the default field value is `undefined` which may cause unexpected `meta.dirty` results.
+
+To get accurate results for the `meta.dirty` flag, you must provide an initial value to your field even if the values are empty.
+
+To reduce the verbosity of adding an `initialValue` prop to each field, you could provide the `initialValues` prop to your `useForm` call instead.
+
+
diff --git a/docs/src/pages/guide/composition-api/getting-started.mdx b/docs/src/pages/guide/composition-api/getting-started.mdx
new file mode 100644
index 000000000..87d8f8082
--- /dev/null
+++ b/docs/src/pages/guide/composition-api/getting-started.mdx
@@ -0,0 +1,307 @@
+---
+layout: ../../../layouts/PageLayout.astro
+title: Getting started
+description: Field-level and form-level validation and validation behavior and error messages with composition API
+order: 1
+next:
+ path: guide/composition-api/handling-forms
+ title: Handling forms
+ description: Submissions, resets and form state
+ intro: |
+ Now that you've learned how to use forms to define fields, collect their values and validate them. Next, you will learn how to handle submissions and implement advanced patterns with forms.
+---
+
+import DocTip from '@/components/DocTip.vue';
+import DocBadge from '@/components/DocBadge.vue';
+import LiveExample from '@/components/LiveExample.vue';
+import Repl from '@/components/MdxRepl.vue';
+
+# Getting started
+
+vee-validate is built from the ground up with the composition API through a collection of functions, mainly the `useField` and `useForm` functions. Internally the `` and `` components actually use the composition functions under the hood.
+
+Meaning you can create your own custom input and form components and they will be treated the same as `` and `` components. You can mix them together and use a `Form` component with any other custom component that uses `useField` and vice versa.
+
+vee-validate supports synchronous and asynchronous validation and allows defining rules on the field-level or the form level using validation schemas. vee-validate has first-class support for
+
+- [yup](https://github.com/jquense/yup) through `@vee-validate/yup` package.
+- [zod](https://github.com/colinhacks/zod) through `@vee-validate/zod` package.
+- [Global validators](/guide/global-validators) (Laravel-like syntax) through `@vee-validate/rules`.
+
+vee-validate has historically been a declarative validation library, and while the composition API changes things a bit, it still follows the same mindset of declarative validation. vee-validate optimizes for building fields and forms, not values.
+
+## When to use composition API
+
+While vee-validate offers both declarative components and composition functions to supercharge your forms, it is always up to you to decide which one to use. However the composition API is easier to integrate and more flexible. You can build custom components with it or integrate it with any UI library. It is generally recommended to use the composition API.
+
+in this page you will learn how to declare forms and how to hook your elements and components into vee-validate forms and achieve value tracking, validation and more.
+
+## Declaring Forms
+
+### Form context
+
+You can declare forms with the `useForm` function exported from the `vee-validate` core package. This is a composition API function that marks the current component as a form.
+
+```vue
+
+
+
+ {{ values }}
+
+```
+
+Calling `useForm` creates a form context in the component and provides it for any child component that injects it. This means you should stick to calling `useForm` once in a component.
+
+Creating a form context does a few things:
+
+- Acts as a value-collector for all the fields you will declare as child components.
+- Validates the fields and aggregates the errors.
+- Aggregates the validity, touched and dirty states of all the fields.
+
+### Field binds
+
+With `useForm` declared, you are now ready to integrate the form with your elements and components. vee-validate is agnostic to the UI you are using.
+
+In the following couple of sections you will learn how to associate your components and elements with the form and how to get value collection, validation, and error messages working.
+
+#### HTML Inputs
+
+`useForm` provides a function called `defineInputBinds`. This function accepts a field path and return an object containing the bindings for the input element. The field path is a string that represents the path to the field in the form context.
+
+For example, if you have a field called `email` in the form context, the field path will be `email`.
+
+```ts
+const { defineInputBinds } = useForm();
+
+const email = defineInputBinds('email');
+```
+
+Here is basic example of how to use `defineInputBinds` with a simple input element:
+
+
+
+Notice that as you type in the input, the `values` are automatically updated with the value changes.
+
+Let's quickly add a validation schema on the form to see some errors on the form. We will be using `yup` throughout the examples, but you can use `zod` or any other supported validation library you want.
+
+To add a `yup` schema or any kind of form schema, you pass it to the `validationSchema` option when calling `useForm`. Naturally, form schemas almost always an `object` or a `shape` schema.
+
+```ts{1,3-5,8}
+import * as yup from 'yup';
+
+const schema = yup.object({
+ email: yup.string().required().email(),
+});
+
+const { defineInputBinds } = useForm({
+ validationSchema: schema,
+});
+
+const email = defineInputBinds('email');
+```
+
+Here is a full running example:
+
+
+
+Notice as you blur the input, the validation is then triggered and the errors are populated. By default `defineInputBinds` optimizes for lazy validation, meaning the validation will be triggered on `change` and on `blur` events.
+
+You can change that behavior, for example you can make it "aggressive" by passing a configuration to `defineInputBinds` to validate on `input` events.
+
+
+
+Now as you type in the field, the input is immediately validated. You can do more with dynamic configurations.
+
+### Component Binds
+
+Similarly to HTML inputs you can achieve the same results with `defineComponentBinds` function. This function accepts a field path and returns an object containing the bindings for the component.
+
+```ts
+const { defineComponentBinds } = useForm();
+
+const email = defineComponentBinds('email');
+```
+
+Following the previous examples we can achieve value tracking like this:
+
+
+
+As you type into the input, notice that the `values` are being updated. This means you don't need to use `v-model` on your inputs or components.
+
+Let's add validation to the previous example:
+
+
+
+Notice that for components, validations are executed immediately. This is intended because component implementations are not standardized across the Vue ecosystem as there is no guarantees it will emit the same range of events as the native HTML elements. However, you can customize the validation trigger if you know the components you are using are emitting the right events to support the behavior.
+
+
+
+In case the component you are working with does not use the `modelValue` prop and the `update:modelValue` event, you can pass `model` option to tell vee-validate which prop and event to use for that component binds. In the following example, the `CustomInput` uses `value` prop and `update:value` event.
+
+
+
+### Mapping attributes and props
+
+With both `defineInputBinds` and `defineComponentBinds` you can map the attributes and props of the in. This is useful when you want to:
+
+- Use a component or an element that doesn't support `v-model`
+- When you want to map the attributes/props to a different name.
+- Pass new attributes/props to the component/element.
+
+You can use `mapProps` or `mapAttrs` (For elements) to include any additional props or attributes you need to add on the component/element.
+
+```ts
+const { defineInputBinds } = useForm({
+ // ...
+});
+```
+
+In the following example we have a component that accepts an `error` string prop and shows that message if it is not empty. This is common in many UI libraries as they try not to lock you in to a specific validation library.
+
+
+
+
+
+The `state` object contains a lot of useful information about the field, it is fully typed so you can explore it with your IDE or visit the [API reference](/api/types#path-state) for more information.
+
+
+
+Notice that `form` also gives you access to `errors` so you can reference them anywhere in the component.
+
+### Dynamic configuration
+
+Instead of passing a static configuration object to either `defineInputBinds` or `defineComponentBinds`, you could pass a function that returns different configuration values. This is useful when you want the configuration to be dynamic based on the field state.
+
+Here is an example that shows how to make the validation behavior "eager". Meaning if the field does not have any errors then it will only validate on `change`.
+
+But once it is invalid, it validates on each input event, making it "eager" for success.
+
+
+
+## Form Schema
+
+As you have seen in the previous examples, the `useForm` function accepts a `validationSchema` that is used to validate the form. We've been using yup to define the schema however you can use `zod` or any 3rd-party validators.
+
+### Validating with Yup
+
+You can pass yup schemas directly like you've seen previously, but it is recommended that you use the `@vee-validate/yup` package. It will enable better support of yup features and unlock full typescript support for your schemas.
+
+```sh
+# with npm
+npm i @vee-validate/yup
+# with pnpm
+pnpm add @vee-validate/yup
+# with yarn
+yarn add @vee-validate/yup
+```
+
+The package exposes a `toTypedSchema` function that you can use to wrap your yup schemas, this allows vee-validate to infer form input and output types. [More on that here](/guide/composition-api/typed-schema).
+
+```ts
+import { useForm } from 'vee-validate';
+import { toTypedSchema } from '@vee-validate/yup';
+import * as yup from 'yup';
+
+// Creates a typed schema for vee-validate
+const schema = toTypedSchema(
+ yup.object({
+ email: yup.string().required().email(),
+ })
+);
+
+const { errors, values } = useForm({
+ validationSchema: schema,
+});
+```
+
+
+
+
+
+There are a couple of optimization caveats when it comes to using `yup` schemas to validate your forms, be sure to check the [best practices guide](/guide/best-practices).
+
+
+
+### Validating with Zod
+
+You can use [Zod](https://github.com/colinhacks/zod) in a very similar manner to how we've been using yup in the past examples, however you will need to add `@vee-validate/zod` to your dependencies.
+
+```sh
+# with npm
+npm i @vee-validate/zod
+# with pnpm
+pnpm add @vee-validate/zod
+# with yarn
+yarn add @vee-validate/zod
+```
+
+Then you can wrap your zod schemas with `toTypedSchema` function which allows vee-validate to infer form input and output types. [More on that here](/guide/composition-api/typed-schema).
+
+```ts
+import { useForm } from 'vee-validate';
+import { toTypedSchema } from '@vee-validate/zod';
+import { z } from 'zod';
+
+// Creates a typed schema for vee-validate
+const schema = toTypedSchema(
+ z.object({
+ email: z.string().required().email(),
+ })
+);
+
+const { errors, values } = useForm({
+ validationSchema: schema,
+});
+```
+
+Here is a full example using zod with `useForm`:
+
+
+
+### Validating with global validators
+
+Another option is using `@vee-validate/rules` which have been historically bundled with past versions of vee-validate. It includes rules that can be defined globally and then used anywhere using Laravel-like string expressions.
+
+You can refer to the full guide on global rules [here](/guide/global-validators).
+
+### Validating with functions
+
+Another option is to just use any 3rd party validation tool you prefer, something like [`validator.js`](https://github.com/validatorjs/validator.js/). Here is a quick example:
+
+
+
+Or you could use any custom function.
+
+
+
+Both `zod` and `yup` are very good at defining schemas especially nested values so it is recommended that you use either. As an added bonus, you get full typescript support with either of them. You can refer to the [Typed schemas guide](/guide/typed-schemas) to learn more about how to maximize the type safety of your schemas.
+
+
+
+### Dynamic Form Schemas
+
+There are a few ways you can create dynamic schemas (reactive) where it changes the validation rules based on some state. The first way to do that is with `computed`.
+
+
+
+When the validation schema value changes, only the fields that were validated at least once will be re-validated, the other fields won't be validated to avoid aggressive validation behavior.
+
+The other ways depend on which validation library you are using, for example with yup you can achieve the same with `yup.lazy` or `zod.lazy`:
+
+
diff --git a/docs/src/pages/guide/composition-api/handling-forms.mdx b/docs/src/pages/guide/composition-api/handling-forms.mdx
index 9d5ed3410..b31e023dc 100644
--- a/docs/src/pages/guide/composition-api/handling-forms.mdx
+++ b/docs/src/pages/guide/composition-api/handling-forms.mdx
@@ -2,54 +2,82 @@
layout: ../../../layouts/PageLayout.astro
title: Handling Forms
description: Handling form state and submissions
-order: 3
-next: guide/composition-api/nested-objects-and-arrays
+order: 2
+next:
+ path: guide/composition-api/nested-objects-and-arrays
+ title: Nested Objects and Arrays
+ description: Structuring form values in nested paths in objects or arrays
+ intro: |
+ So far we've only dealt with flat form values, what about nested objects and arrays? In the next guide you will learn how to use path names to structure your values and nest them declaratively.
---
import DocTip from '@/components/DocTip.vue';
+import Repl from '@/components/MdxRepl.vue';
# Handling Forms
-vee-validate offers many helpers to handle form submissions, resets, and DX to make your forms much easier to reason about and less of a burden to maintain. The `useForm` function allow you to easily handle:
+vee-validate makes it easy to handle form submissions, resets, and DX to make your forms much easier to reason about and less of a burden to maintain. The `useForm` function allow you to easily handle:
-- Submitting forms with JavaScript listeners (AJAX)
-- Submitting forms using the classic/native approach (page reload)
-- Handling form resets
+- Form state (valid/dirty/touched/pending).
+- Submitting forms and handling invalid submissions.
+- Handling form resets.
-## Handling Submissions
+## Form Metadata
-Before we get to submitting forms, there are a few things to keep in mind.
+Forms have a `meta` object value containing useful information about the form, it acts as an aggregation of the metadata for the fields inside that form.
-The main goal of the composition API is to enable you to add validation logic to your components and UI. So in real-world examples `useField` will be called in `TextField` and `SelectField` components, while `useForm` will be called in their parent form component.
+```js
+const { meta } = useForm();
-vee-validate exposes useful defaults to help you handle form submissions whether you submit them using JavaScript or native HTML submissions, in most cases, you would like to make sure all your fields are valid before you submit the form, this is done for you by default.
+meta.value.dirty;
+meta.value.pending;
+meta.value.touched;
+meta.value.valid;
+meta.value.initialValues;
+```
-### JavaScript Submissions (AJAX)
+- `valid`: The form's validity status, will be `true` if the errors array is empty initially, but will be updated once the form is mounted.
+- `touched`: If at least one field was blurred (unfocused) inside the form.
+- `dirty`: If at least one field's value was updated.
+- `pending`: If at least one field's validation is still pending.
+- `initialValues`: All fields' initial values, packed into an object where the keys are the field names.
-Mainly you will be using the `handleSubmit` function to create submission handlers for your forms, the `handleSubmit` function accepts a callback that receives the final form values, which is very convenient since you don't normally have access to the form values in the parent component.
+Here is a simple example where we disable the form's submit button unless a field was touched.
-```vue
-
-
-
+
-
```
+Here is an example that makes use of `handleSubmit` to validate before submitting the form.
+
+
+
The `handleSubmit` function will only execute your callback once the returned function (`onSubmit` in the example) if all fields are valid, meaning you don't have to handle if the form is invalid in your logic.
You can call the returned function either manually or via an event like `@submit` and it will validate all the fields and execute the callback if everything passes validation.
@@ -58,140 +86,123 @@ As a bonus, when the returned function is used as an event handler (like in the
### Full-Page Submissions (non-AJAX)
-For non-ajax submissions that trigger a full page reload, you can use the `submitForm` function instead of `handleSubmit`. You normally would use this if you are not building a single-page application.
-
-```vue
-
-
-
-
-
-```
+
In that case **YOU MUST** use `submitForm` as an event handler for the `submit` event for a native `form` element, otherwise, it would have no effect.
### Handling Invalid Submissions
-In case you want to perform some logic after a form fails to submit due to validation errors (e.g: focusing the first invalid field), you can pass a callback as the second argument to `handleSubmit` function.
-
-```vue{12-16,22}
-
-
-
-
-
+// This handles both valid and invalid submissions
+const onSubmit = handleSubmit(onSuccess, onInvalidSubmit);
```
-## Submission Progress
+Here is a quick example on how to scroll to and focus the first invalid field after a failed submission attempt.
+
+
+
+### Submission Progress
Quite often you need to show your users a submission indicator, or you might want to disable the submit button entirely until the submission attempt is done. The `useForm` function exposes an `isSubmitting` ref that you can use.
The `isSubmitting` state will be set to `true` once the validation of the form starts (as a result of a submit event) and will keep track of the submission handler you passed to either `onSubmit` or until it calls `submitForm`. If the submission handler throws any errors or completes successfully it will be set to `false` afterward.
-```vue
-
-
-
+
-
+```js
+const { submitCount } = useForm();
```
-## Submit Count
+Maybe you want to lock the form if too many attempts were made, or you want to show a message after the first submission attempt.
-The `useForm` function also exposes a `submitCount` ref that you can use to track the number of submissions attempted by the user. Maybe you want to lock the form if too many attempts were made.
+
-```vue
-
-
+vee-validate does the following when calling submission handlers created by `handleSubmit` or when calling `submitForm` as a result of the user submitting the form.
- Sorry but you have attempted to submit too many times
-
+#### Before validation stage
-
+#### After validation stage
+
+- Calls the `handleSubmit` handler you passed
+- After the callback finishes (it will wait if the result is asynchronous), then it will set `isSubmitting` to `false`
+
+Note that there isn't a need to have `isSubmitting` set back to false if you've used `submitForm`, as this submission method will perform a full-page refresh (native forms behavior).
+
+## Form Values
+
+You may have noticed in the earlier examples that you can access all fields' values using the `values` reactive object returned by `useForm`.
+
+```js
+const { values } = useForm();
```
-## Initial Values
+The `values` object is read-only and should not be mutated with a `v-model` or by assigning a value to it. This is because all mutations are done through the vee-validate API, this is because mutations to form state needs to have a context.
-Since with vee-validate you don't have to use `v-model` to track your values, the `useForm` function allows you to define the starting values for your fields, by default all fields start with `undefined` as a value.
+For example:
-Using the `initialValues` option you can send an object that contains the field names as keys and their values:
+```js
+const { values } = useForm();
-```vue
-
-
-
+// ❌ Do not do that!
+values.email = '';
+```
-
```
-All fields created with `useField` or `` component will then grab their initial value from the object you passed to `useForm`.
+
@@ -199,85 +210,99 @@ It's generally recommended that you provide the `initialValues`, this is because
-## Setting Form Values
+
+
+If you are using `zod` or `yup` with [Typed schemas](/guide/composition/typed-schema), you can define the initial values on the validation schema directly with `.default` instead of having to specify `initialValues`.
-You can set any field's value on the form-level component using either `setFieldValue` or `setValues` returned by `useForm`.
+
-```vue
-
-
-
+### Manually Setting Form Values
-
```
-## Submission Behavior
+
-vee-validate does the following when calling submission handlers created by `handleSubmit` or when calling `submitForm` as a result of the user submitting the form.
+### Controlled Values
-### Before validation stage
+The form values can be categorized into two categories:
-- Sets all fields `touched` meta to `true`
-- Sets `isSubmitting` form state to `true`
-- Increments the `submitCount` form state by `1`
+- Controlled values: values that have a form input controlling them via `defineInputBinds` or `defineComponentBinds` or `useField` or ``.
+- Uncontrolled values: values that are inserted dynamically with `setFieldValue` or inserted initially with initial values.
-### Validation stage
+Sometimes you maybe only interested in controlled values. For example, your initial data contains noisy extra properties from your API and you wish to ignore them when submitting them back to your API.
-- Sets form and individual fields meta `pending` to `true` to indicate validation is in progress
-- Runs the validation function/schema/rule against the current form values asynchronously
-- Checks for any errors in the validation result
- - If there are errors then it will skip the next stage and update the validation state (meta, errors) for the form and fields
- - If there aren't any errors then it will set the `pending` meta flag to `false` and proceed to the next stage
+When accessing `values` from `useForm` result or the submission handler you get all the values, both controlled and uncontrolled values. To get access to only the controlled values you can use `controlledValues` from the `useForm` result:
-### After validation stage
+```js
+const { handleSubmit, controlledValues } = useForm();
-- Calls the `handleSubmit` handler you passed
-- After the callback finishes (it will wait if the result is asynchronous), then it will set `isSubmitting` to `false`
+const onSubmit = handleSubmit(async () => {
+ // Send only controlled values to the API
+ // Only fields declared with `useField` or `useFieldModel` will be sent
+ const response = await client.post('/users/', controlledValues.value);
+});
+```
-Note that there isn't a need to have `isSubmitting` set back to false if you've used `submitForm`, as this submission method will perform a full-page refresh (native forms behavior).
+Alternatively for less verbosity, you can create submission handlers with only the controlled values with `handleSubmit.withControlled` which has the same API as `handleSubmit`:
-## Handling Resets
+```js
+const { handleSubmit } = useForm();
+
+const onSubmit = handleSubmit.withControlled(async values => {
+ // Send only controlled values to the API
+ // Only fields declared with `useField` or `useFieldModel` will be sent
+ const response = await client.post('/users/', values);
+});
+```
-vee-validate also handles form resets in a similar way to submissions. When resetting the form, all fields' errors and meta flags will be reset to their original state, including the fields' values.
+Here is an example that filters out some noisy initial values when submitting the form using `withControlled` modifier.
-To reset forms you can use the `resetForm` function returned by `useForm`.
+
-```vue
-
-
-
+## Handling Resets
-
-
-
+vee-validate also handles form resets in a similar way to submissions. When resetting the form, all fields' errors will be cleared, and meta info will be reset to defaults and the values will be reset to their original or initial values.
-
+
+// Rests the form
+resetForm();
+
+//
+resetForm({
+ touched: {
+ email: false,
+ },
+ errors: {
+ email: 'custom error',
+ },
+ values: {
+ email: 'newvalue@email.com',
+ },
+});
```
-The `resetForm` accepts an optional `state` object that allows you to specify the new initial values for any of the fields state, this is the shape of the `FormState` object:
+This is the shape of the `FormState` object:
```ts
interface FormState {
// any error messages
errors: Record;
- // dirty meta flags
- dirty: Record;
// touched meta flags
touched: Record;
// Form Values
@@ -285,16 +310,9 @@ interface FormState {
}
```
-In the following snippet, `resetForm` is used to update the form values to specific ones other than their original values. This is useful if your receive your form state asynchronously
+Here is an example where a full-form is being reset:
-```js
-resetForm({
- values: {
- email: 'example@example.com',
- password: '',
- },
-});
-```
+
### Resetting Forms After Submit
@@ -314,53 +332,39 @@ export interface FormActions {
This is an example of using the form actions object to reset the form:
-```vue
-
-
-
+
-
+```js
+const { errors } = useForm();
```
-## Initial Errors
+However, if you want to display multiple errors for your fields then you can use `errorBag` which is a mapping of each field's path an array of error messages for that field.
-If you are building a non-SPA application it is very common to pre-fill form errors using server-side rendering, frameworks like Laravel and Rails make this very easy to do. vee-validate supports filling the errors initially before any validation is done using the `initialErrors` option.
+```js
+const { errorBag } = useForm();
+```
-The `initialErrors` option accepts an object containing the field names as keys with their corresponding error message string.
+Here is an example that displays multiple errors for a field:
+
+
-```vue
-
-
-
+### Initial Errors
-
```
@@ -369,7 +373,7 @@ const { value, errorMessage } = useField('email');
-## Setting Errors Manually
+### Setting Errors Manually
Quite often you will find yourself unable to replicate some validation rules on the client-side due to natural limitations. For example, `unique` email validation is complex to implement on the client-side, which the ability to set errors manually can be useful.
@@ -377,16 +381,7 @@ You can set messages for fields by using either `setFieldError` which sets an er
Both functions are available as a return value from `useForm`. In the following example, we check if the server response contains any validation errors and we set them on the fields:
-```vue
-
-
-
-
-
```
Alternatively you can use the `FormActions` passed as the second argument to the `handleSubmit` callback which contains both functions for convenience:
@@ -429,55 +423,6 @@ const onSubmit = handleSubmit(async (values, actions) => {
});
```
-## Controlled Values
-
-The form values can be categorized into two categories:
-
-- Controlled values: values that have a form input controlling them via `useField` or `` or via `useFieldModel` model binding.
-- Uncontrolled values: values that are inserted dynamically with `setFieldValue` or inserted initially with initial values.
-
-Sometimes you maybe only interested in controlled values. For example, your initial data contains noisy extra properties from your API and you wish to ignore them when submitting them back to your API.
-
-When accessing `values` from `useForm` result or the submission handler you get all the values, both controlled and uncontrolled values. To get access to only the controlled values you can use `controlledValues` from the `useForm` result:
-
-```vue
-
-
-
-
-
-```
-
-Alternatively for less verbosity, you can create handlers with only the controlled values with `handleSubmit.withControlled` which has the same API as `handleSubmit`:
+Here is an example that sets form errors after submission, usually you will have a backend API that returns the errors:
-```vue
-
-
-
-
-
-```
+
diff --git a/docs/src/pages/guide/composition-api/helpers.mdx b/docs/src/pages/guide/composition-api/helpers.mdx
new file mode 100644
index 000000000..89579dcac
--- /dev/null
+++ b/docs/src/pages/guide/composition-api/helpers.mdx
@@ -0,0 +1,44 @@
+---
+layout: ../../../layouts/PageLayout.astro
+title: Composition Helpers
+description: Composition API helpers
+menuTitle: Helpers
+order: 5
+next:
+ path: guide/composition-api/typed-schema
+ title: TypeScript - Typed Schemas
+ description: Input and output types and how to make the most out of your form schemas with TypeScript.
+ intro: |
+ For TypeScript users, it is imperative that they strongly type their form values and the their submissions. This guide will show you how to use either zod or yup with vee-validate's typed schema utilities to get the most out of your schemas with TypeScript.
+---
+
+Aside from `useField` and `useForm` and `useFieldArray`, these are a collection of simple functions that you can use to opt-in specific parts of vee-validate features like form state and various actions you can perform on fields and forms.
+
+Here are a few examples of what you can build with these functions:
+
+- A custom submission progress component
+- A custom error message component.
+- A form validity indicators
+- reset buttons or submit buttons
+
+Here is a list of the functions available that you can use:
+
+- `useFieldError` Gives access to a single field's first error message
+- `useFormErrors` Gives access to the entire error bag of the form
+- `useIsFieldDirty` If a field is dirty
+- `useIsFormDirty` If the form is dirty (form contains at least one dirty field)
+- `useIsFieldTouched` If a field is touched
+- `useIsFormTouched` If the form is touched (form contains at least one touched field)
+- `useIsFieldValid` If a field is valid
+- `useIsFormValid` If all fields are **validated and valid**
+- `useValidateField` Returns a function that validates a specific field
+- `useValidateForm` Returns a function that validates the entire form
+- `useResetForm` Resets the form to its initial state
+- `useSubmitForm` Creates a submission function that validates and submits the form (even if no `form` element is involved)
+- `useIsSubmitting` If the form is currently submitting
+- `useIsValidating` If the form is currently validating by validate function
+- `useSubmitCount` The number of times the user attempted to submit the form
+- `useFieldValue` Returns a specific fields' current value
+- `useFormValues` Returns the current form field values
+
+For more information about the functions, you can head over to the [API reference and check them out](/api/composition-helpers).
diff --git a/docs/src/pages/guide/composition-api/nested-objects-and-arrays.mdx b/docs/src/pages/guide/composition-api/nested-objects-and-arrays.mdx
index f67d32977..564509b13 100644
--- a/docs/src/pages/guide/composition-api/nested-objects-and-arrays.mdx
+++ b/docs/src/pages/guide/composition-api/nested-objects-and-arrays.mdx
@@ -1,43 +1,33 @@
---
layout: ../../../layouts/PageLayout.astro
title: Nested Objects and Arrays
-description: Structuring forms in nested paths in objects or arrays
-order: 4
-next: guide/composition-api/typed-schema
+description: Structuring form values in nested paths in objects or arrays
+order: 3
+next:
+ path: guide/composition-api/custom-inputs
+ title: Custom Inputs
+ description: Building input components with the composition API
+ intro: |
+ You've learned a lot about forms so far, but that's not all. Just like how vee-validate makes it easy to build forms, it also offers tools to help you build custom input components with the composition API. Enter `useField()`.
---
import DocTip from '@/components/DocTip.vue';
import DocBadge from '@/components/DocBadge.vue';
+import Repl from '@/components/MdxRepl.vue';
# Nested Objects and Arrays
-vee-validate supports nested objects and arrays by using field name syntax to indicate a field's path. This allows you to structure forms easily to make data mapping straightforward without having to deal with flat form values.
+vee-validate supports nested objects and arrays by using field path syntax to indicate a field's path. This allows you to structure forms easily to make data mapping straightforward without having to deal with flat form values.
## Nested Objects
You can specify a field to be nested in an object using dot paths, like what you would normally do in JavaScript to access a nested property. The field's `name` acts as the path for that field in the form values:
-```vue
-
-
-
-
-
+const twitterLink = defineInputBinds('links.twitter');
+const githubLink = defineInputBinds('links.github');
```
Submitting the previous form would result in the following values being passed to your handler:
@@ -51,36 +41,26 @@ Submitting the previous form would result in the following values being passed t
}
```
+Here is a live example so you can see it in action.
+
+
+
You are not limited to a specific depth, you can nest as much as you like.
## Nested Arrays
Similar to objects, you can also nest your values in an array, using square brackets just like how you would do it in JavaScript.
-Here is the same example as above but in array format:
-
-```vue
-
-
-
+```js
+const { defineInputBinds } = useForm();
-
-```
+
Submitting the previous form would result in the following values being passed to your handler:
@@ -103,27 +83,11 @@ vee-validate will only create nested arrays if the path expression is a complete
If your fields' names are using the dot notation and you want to avoid the nesting behavior which is enabled by default, all you need to do is wrap your field names in square brackets (`[]`) to disable nesting for those fields.
-```vue
-
-
-
-
-
+const twitter = defineInputBinds('[links.twitter]');
+const github = defineInputBinds('[links.github]');
```
Submitting the previous form would result in the following values being passed to your handler:
@@ -135,51 +99,35 @@ Submitting the previous form would result in the following values being passed t
}
```
-## Field Arrays
-
-Field arrays are a special type of nested array fields, they are often used to collect repeatable pieces of data or repeatable forms. They are often called "repeatable fields".
+Here is a live example on that:
-Unlike the [components](/guide/components/nested-objects-and-arrays) API, it can be tricky to set up a group of repeatable fields with the composition API in the same component. This is because you usually need an input component to iterate over.
+
-The following snippet uses the `Field` component as the input component, but you can use any component as long as they call `useField` internally.
+## Field Arrays
-To set up a repeatable field, you can use `useFieldArray` to help you manage the array values and operations:
+Field arrays are a special type of nested array fields, they are often used to collect repeatable pieces of data or repeatable forms. They are often called "repeatable fields".
-```vue
-
-
-
+const { remove, push, fields } = useFieldArray('users');
+```
-
-```
+Notice that we used `field.key` as the key of the iteration, this is because vee-validate generates a unique key for each array item so you can rely it on without having to generate them yourself.
### Field Array Paths
-When planning to use `useFieldArray` you need to provide a `name` prop which is the path of the array starting from the root form value, you can use dot notation for object paths or indices for array paths.
+When calling `useFieldArray` you need to provide a `name` prop which is the path of the array starting from the root form value, you can use dot notation for object paths or indices for array paths.
Here are a few examples:
@@ -195,34 +143,6 @@ _*Iterate over the `domains` inside `settings.dns` object:*_
const { remove, push, fields } = useFieldArray('settings.dns.domains');
```
-### Iteration Keys
-
-The `FieldArrayEntry` item exposes a `key` property, this property is unique and is auto-generated for you so you can use it as an iteration key.
-
-```vue
-
-
-
-
-
-```
-
-This auto-generated `key` property is very convenient as you no longer have to provide your own unique key for each item.
-
### Array Helpers
The `` function provides the following properties and functions:
@@ -273,44 +193,10 @@ const field = useField('field', undefined, {
When referencing errors using `errors` object returned from the `useForm` function. Make sure to reference the field name in the same way you set it on the `name` argument for that field. So even if you avoid nesting you should always include the square brackets. In other words `errors` do not get nested, they are always flat.
-### Nested Fields With Validation Schema
-
-Since vee-validate supports [form-level validation](/guide/composition-api/validation#form-level-validation), referencing the nested fields may vary depending on how you are specifying the schema.
-
-If you are using yup, you can utilize the nested `yup.object` or `yup.array` schemas to provide validation for your nested fields, here is a quick example:
-
-```vue
-
-
-
+Since vee-validate supports `yup` and `zod` schemas, referencing the nested fields may vary depending on how you are specifying the schema.
-
-```
+
You can [visit this link](/examples/array-fields) for a practical example using nested arrays.
diff --git a/docs/src/pages/guide/composition-api/typed-schema.mdx b/docs/src/pages/guide/composition-api/typed-schema.mdx
index f61e5b054..6c2d51a56 100644
--- a/docs/src/pages/guide/composition-api/typed-schema.mdx
+++ b/docs/src/pages/guide/composition-api/typed-schema.mdx
@@ -2,10 +2,13 @@
layout: ../../../layouts/PageLayout.astro
title: Typed Schemas
description: Type-safe input and output types
-order: 5
-next: guide/composition-api/api-review
-menuTitle: Caveats
-new: true
+order: 6
+next:
+ title: Caveats and best practices
+ path: guide/composition-api/caveats
+ description: Things to keep in mind when using the composition API and some recommendations to follow.
+ intro: |
+ This next guide covers the weird parts and offers recommendations into how to use vee-validate's composition API effectively.
---
import DocTip from '@/components/DocTip.vue';
diff --git a/docs/src/pages/guide/composition-api/validation.mdx b/docs/src/pages/guide/composition-api/validation.mdx
deleted file mode 100644
index 42cdb239c..000000000
--- a/docs/src/pages/guide/composition-api/validation.mdx
+++ /dev/null
@@ -1,695 +0,0 @@
----
-layout: ../../../layouts/PageLayout.astro
-title: Validation
-description: Field-level and form-level validation and validation behavior and error messages with composition API
-order: 2
-next: guide/composition-api/handling-forms
----
-
-import DocTip from '@/components/DocTip.vue';
-import DocBadge from '@/components/DocBadge.vue';
-import LiveExample from '@/components/LiveExample.vue';
-
-# Validation
-
-vee-validate handles complex validations in a very easy way, it supports synchronous and asynchronous validation and allows defining rules on the field-level or the form level using validation schemas with built-in support for [yup](https://github.com/jquense/yup).
-
-You will be using the following composition functions to validate your forms:
-
-- `useField`: Creates a form field with its validation state, you will use this inside your custom input components.
-- `useForm`: Creates a vee-validate's form context and associates any fields created with `useField` 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.
-
-There are other composition API functions, check them out in the [API reference](/api/composition-helpers).
-
-This is the most basic example with the composition API, you can create a simple field and validate it in a couple of lines:
-
-
-
-## Field-level Validation
-
-You can define validation rules for your fields using the `useField` composition API function, your rules can be as simple as a function that accepts the current value and returns an error message. Here is an example where you can use `useField` to create a custom input component called `MyTextInput`.
-
-```vue
-
-
-
- {{ errorMessage }}
-
-
-
-
-```
-
-The validation happens automatically when `value` binding changes, meaning you can use `useField` to trigger validation for any kind of data and not just for inputs.
-
-### Validating fields with yup
-
-yup is a very popular, simple and powerful data validation library for JavaScript, you can use it in combination with vee-validate, You can use [`yup`](https://github.com/jquense/yup) to define your validation rules for that field:
-
-```vue
-
-
-
- {{ errorMessage }}
-
-
-
-
-```
-
-For more information on the `useField` function, read [the API reference](/api/use-field).
-
-## Form-level Validation
-
-Instead of providing validations for each field individually which can clutter your code, you can define the validation schema using the `useForm` function by passing a `validationSchema` option. Each field will automatically be associated with it and will be validated accordingly.
-
-A simple validation schema can be an object containing field names as keys and validation functions as the value for those keys.
-
-```vue
-
-
-
-
-
-
-
-
-
-```
-
-Note that `MyTextInput` is the component we created earlier except it doesn't provide any rules to `useField` because `useForm` will automatically figure it out.
-
-### Validation schemas with yup
-
-Fortunately there is already a very neat way to build validation schemas for your forms by using `yup`, it allows you create validation objects like this:
-
-```js
-const schema = yup.object({
- email: yup.string().required().email(),
- name: yup.string().required(),
- password: yup.string().required().min(8),
-});
-```
-
-vee-validate has built-in support for yup schemas, You can pass your schemas to the `useForm` function using the same `validationSchema` option:
-
-```vue
-
-
-
-
-
-
-
-
-
-```
-
-For more information on the `useForm` function, read [the API reference](/api/use-form).
-
-
-
-There are a couple of optimization caveats when it comes to using `yup` schemas to validate your forms, be sure to check the [best practices guide](/guide/best-practices).
-
-
-
-
-
-There is an official integration available for [Zod validation](https://github.com/colinhacks/zod) that you can use as a drop-in replacement for `yup`. Check the [zod integration page](/integrations/zod-schema-validation).
-
-
-
-### Reactive Form Schema
-
-You can have reactive form schemas using `computed` if you are looking to create dynamic schemas using either `yup` or a validation object.
-
-```vue
-
-
-
-
-
-
-
-```
-
-When the validation schema changes, only the fields that were validated at least once will be re-validated, the other fields won't be validated to avoid aggressive validation behavior.
-
-## Validation Behavior
-
-
-
-This is only relevant to the `useField` and `useForm` API
-
-
-
-By default vee-validate runs validation whenever the `value` ref changes whether it was bound by a `v-model` or changed in your code:
-
-```js
-const { value } = useField('fieldName', isRequired);
-
-// validation WILL be triggered
-value.value = 'something';
-```
-
-You can disable that behavior by passing a `validateOnValueUpdate` option set to `false`:
-
-```js
-const { value } = useField('fieldName', isRequired, {
- validateOnValueUpdate: false,
-});
-
-// validation WILL NOT trigger
-value.value = 'something';
-```
-
-### Handling Events
-
-`useField()` composition function is not concerned with any events, it only validates whenever the `value` ref changes. However, it gives you everything you need to set up your own validation experience.
-
-The `useField` function exposes some handler functions, each handles a specific aspect of the validation experience:
-
-- `handleChange`: Updates the field value, can be configured to trigger validation or silently update the value
-- `handleBlur`: Updates the `meta.touched` flag, doesn't trigger validation.
-
-```js
-const { handleChange, handleBlur } = useField('someField');
-```
-
-In this example we are validating on `input` event (when the user types), which would make the validation aggressive:
-
-```vue
-
-
-
- {{ errorMessage }}
-
-
-
-
-```
-
-With a slight adjustment we can make our validation lazy by changing the listener to `@change` (validates when the user leaves the control):
-
-```vue-html
-
-
- {{ errorMessage }}
-
-```
-
-If we wanted to validate on `blur` as well, you can simply add `handleChange` as a handler for it:
-
-```vue-html
-
-
- {{ errorMessage }}
-
-```
-
-As you can see, the `useField` doesn't care which events you use `handleChange` for. This allows for greater flexibility that's not possible with the `` component, not as straightforward at least.
-
-Consider this validation experience:
-
-- Validate on Change/Blur initially (when user leaves the control), let's call this lazy mode.
-- If the field is invalid, switch the validation to validate on input (when user types), let's call this aggressive mode.
-- If the field is valid, go back to "lazy" mode, otherwise be "aggressive".
-
-Implementing this requires some knowledge about how the `v-on` (we can bind objects on it) handler works and the `computed` function. It can be as easy as this:
-
-```vue
-
-
-
- {{ errorMessage }}
-
-
-
-
-```
-
-Check the working example here:
-
-
-
-## Displaying Error Messages
-
-You can display error messages using either `useField` or `useForm`.
-
-### Displaying Errors with useField
-
-You've already seen how to display errors with `useField`, by using the `errorMessage` ref:
-
-```vue
-
-
-
- {{ errorMessage }}
-
-
-
-
-```
-
-In addition to this, you can get all errors for the field using the `errors` ref which contains multiple error messages if applicable:
-
-```vue
-
-
-
-
-
- Please correct these errors:
-
- - {{ message }}
-
-
-
-
-
-
-```
-
-### Displaying Errors with useForm
-
-You can use the `errors` returned by `useForm` to display messages for all fields.
-
-```vue
-
-
-
-
-
-```
-
-### Custom Field Labels
-
-More often than not, your fields will have names with underscores or shorthands which isn't very nice when showing in error messages, for example, you might have specific encoding to your field names because they might be generated by the backend. Ideally, you want to avoid having messages like:
-
-```txt
-The down_p is required
-```
-
-And instead show something more meaningful to the user
-
-```txt
-The down payment is required
-```
-
-You can do this in two ways depending on which validators you are using (yup or [global validators](/guide/global-validators)).
-
-#### Custom Labels with Yup
-
-With yup it is very straightforward, you just need to call `label()` after defining your field's validations either in field level or form level:
-
-```js
-const schema = Yup.object({
- email_addr: Yup.string().email().required().label('Email Address'),
- acc_pazzword: Yup.string().min(5).required().label('Your Password'),
-});
-```
-
-If you are interested in how to do the same for global validators check the [i18n guide](/guide/i18n#custom-labels)
-
-## Validation Metadata
-
-### Field-level Meta
-
-Each field has metadata associated with it, the `meta` property returned from `useField` contains information about the field:
-
-- `valid`: The current field validity, automatically updated for you.
-- `touched`: If the field was **touched**, can be updated with `setTouched` on `useField` return value.
-- `dirty`: If the field value was updated, you cannot change its value.
-- `pending`: If the field's validations are still running, useful for long-running async validation.
-- `initialValue`: The field's initial value, is `undefined` if you didn't specify any.
-
-```js
-const { meta } = useField('fieldName');
-
-meta.dirty;
-meta.pending;
-meta.touched;
-meta.valid;
-meta.initialValue;
-```
-
-This is the typescript interface for a field's meta value
-
-```ts
-interface FieldMeta {
- dirty: boolean;
- pending: boolean;
- touched: boolean;
- valid: boolean;
- initialValue: any;
-}
-```
-
-In the following example, we use the `meta.dirty` flag to check if the field value was changed or not
-
-```vue
-
-
-
-
-
-
-
-```
-
-
-
-Notice in the previous example, we passed an `initialValue`, this is because the default field value is `undefined` which may cause unexpected `meta.dirty` results.
-
-To get accurate results for the `meta.dirty` flag, you must provide an initial value to your field even if the values are empty.
-
-To reduce the verbosity of adding an `initialValue` prop to each field, you could provide the `initialValues` prop to your `useForm` call instead.
-
-
-
-
-
-Since the `meta.valid` flag is initially `true` (because it just means there are no errors yet), it would cause problems if you have a "success" UI state an indicator.
-
-To avoid this case you should combine the `valid` flag with either `meta.dirty` or `meta.touched` to get an accurate representation.
-
-
-
-### Form-level Meta
-
-Forms also have their own `meta` value containing useful information about the form, it acts as an aggregation of the metadata for the fields inside that form.
-
-```js
-const { meta } = useForm();
-
-meta.value.dirty;
-meta.value.pending;
-meta.value.touched;
-meta.value.valid;
-meta.value.initialValues;
-```
-
-- `valid`: The form's validity status, will be `true` if the errors array is empty initially, but will be updated once the form is mounted.
-- `touched`: If at least one field was blurred (unfocused) inside the form.
-- `dirty`: If at least one field's value was updated.
-- `pending`: If at least one field's validation is still pending.
-- `initialValues`: All fields' initial values, packed into an object where the keys are the field names.
-
-Here is a similar example where we disable the form's submit button if no value was changed, the same caveat applies is that we have to provide an `initialValues` prop to `useForm` to make sure `meta.dirty` works accurately:
-
-```vue
-
-
-
-
-
-```
-
-### `useField` Model Events
-
-Quite often you will setup your component to [support `v-model` directive](https://vuejs.org/guide/components/events.html#usage-with-v-model) by adding a `modelValue` prop and emitting a `update:modelValue` event whenever the value changes.
-
-If you use `useField` in your input component you don't have to manage that yourself, it is automatically done for you. Whenever the `useField` value changes it will emit the `update:modelValue` event and whenever the `modelValue` prop changes the `useField` value will be synced and validated automatically.
-
-The only requirement is you need to define your model prop on your component explicitly.
-
-```vue
-
-```
-
-You can change the default model prop name by passing `modelPropName` option to `useField`:
-
-```vue
-
-```
-
-You can also disable this behavior completely and manage those events yourself by passing `syncVModel` option to `useField`:
-
-```vue
-
-```
-
-### Binding `v-model` directly to Form Values
-
-If you want to skip using `useField` due to its verbosity or thinking that creating a custom input component is an overkill for your needs, there is a simpler alternative. You can use `useFieldModel` returned from `useForm` to bind your input elements directly and validate them.
-
-```vue
-
-
- {{ errors.email }}
-
-
- {{ errors.password }}
-
-
-
-```
-
-The downside of using `useFieldModel` instead of `useField` is you lose a few field-specific features like validation trigger handling and field-level meta information. This means all errors for these models will be generated whenever the value changes.
diff --git a/docs/src/pages/index.astro b/docs/src/pages/index.astro
index b272175bf..218c62dc4 100644
--- a/docs/src/pages/index.astro
+++ b/docs/src/pages/index.astro
@@ -2,7 +2,7 @@
import HomeLayout from '@/layouts/HomeLayout.astro';
import SponsorButton from '@/components/SponsorButton.vue';
import Icon from '@/components/Icon.vue';
-import MainPageExample from '@/components/examples/MainPageExample.vue';
+import MainPageExample from '@/components/MainPageExample.vue';
import StarCount from '@/components/StarCount.vue';
import { features } from '@/constants';
import UiLibraries from '@/components/UiLibraries.vue';
@@ -132,6 +132,6 @@ const highlights = [
In this example, value tracking, validation and submissions are handled in just a few lines of code
-
+
diff --git a/docs/src/pages/submit-target.astro b/docs/src/pages/submit-target.astro
new file mode 100644
index 000000000..32efd17a0
--- /dev/null
+++ b/docs/src/pages/submit-target.astro
@@ -0,0 +1,16 @@
+---
+import LogQueryStr from '@/components/LogQueryStr.vue';
+---
+
+
+
+ Submit target
+
+
+
+
+
+
+
+
+
diff --git a/docs/src/styles/page.css b/docs/src/styles/page.css
index 815b6a7e8..8dee8c1bf 100644
--- a/docs/src/styles/page.css
+++ b/docs/src/styles/page.css
@@ -1,6 +1,6 @@
.PageApp {
@apply h-full mx-auto;
- max-width: 1500px;
+ max-width: 1600px;
grid-template-areas:
'content'
'footer';
@@ -11,7 +11,7 @@
grid-template-areas:
'lside content rside'
'footer footer footer';
- grid-template-columns: 1.5fr 45rem 0.5fr;
+ grid-template-columns: 1.5fr 50rem 0.5fr;
column-gap: 100px;
}
@@ -19,7 +19,7 @@
grid-template-areas:
'lside content rside'
'footer footer footer';
- grid-template-columns: 1fr 45rem 1fr;
+ grid-template-columns: 1fr 50rem 1fr;
}
.main {
diff --git a/docs/src/utils/examples.ts b/docs/src/utils/examples.ts
new file mode 100644
index 000000000..4b2eb3d78
--- /dev/null
+++ b/docs/src/utils/examples.ts
@@ -0,0 +1,63 @@
+import { Project } from '@stackblitz/sdk';
+import { version } from '../../../packages/vee-validate/package.json';
+
+export function getViteProjectConfig(files: Record): Project {
+ return {
+ template: 'node',
+ title: `vee-validate example`,
+ files: {
+ 'package.json': `{
+ "name": "vite-vue-starter",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@vee-validate/zod": "^${version}",
+ "@vee-validate/yup": "^${version}",
+ "@vee-validate/rules": "^${version}",
+ "@vee-validate/i18n": "^${version}",
+ "vee-validate": "^${version}",
+ "vue": "^3.3.4",
+ "yup": "latest",
+ "zod": "latest"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-vue": "^4.2.3",
+ "vite": "^4.3.9"
+ }
+}`,
+ 'index.html': `
+
+
+
+
+
+ Vite + Vue
+
+
+
+
+
+`,
+ 'src/main.js': `import { createApp } from 'vue'
+ import App from './App.vue'
+
+ createApp(App).mount('#app')
+ `,
+ 'vite.config.js': `import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [vue()],
+})
+`,
+ ...Object.fromEntries(Object.entries(files).map(([file, content]) => [`src/${file}`, content])),
+ },
+ };
+}
diff --git a/docs/src/utils/seo.ts b/docs/src/utils/seo.ts
index c70285732..6fdc7796f 100644
--- a/docs/src/utils/seo.ts
+++ b/docs/src/utils/seo.ts
@@ -155,7 +155,16 @@ export function generateLinks({ url }) {
export interface Frontmatter {
title: string;
+ menuTitle?: string;
order: number;
+ next?: {
+ title: string;
+ description: string;
+ path: string;
+ intro: string;
+ };
+ description?: string;
+ path?: string;
}
export function buildMenu(pages: { url?: string; frontmatter: Frontmatter; children?: any[]; icon?: string }[]) {
@@ -166,7 +175,10 @@ export function buildMenu(pages: { url?: string; frontmatter: Frontmatter; child
].map(p => {
return {
title: p.frontmatter.title,
+ menuTitle: p.frontmatter.menuTitle,
order: p.frontmatter.order,
+ description: p.frontmatter.description,
+ next: p.frontmatter.next,
path: p.url || '',
children: p.children ? buildMenu(p.children) : undefined,
icon: p.icon || undefined,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2015c3b2f..486563adf 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -161,6 +161,18 @@ importers:
'@types/fs-extra':
specifier: ^11.0.1
version: 11.0.1
+ '@types/lodash-es':
+ specifier: ^4.17.7
+ version: 4.17.7
+ '@vee-validate/yup':
+ specifier: 4.9.6
+ version: 4.9.6(vue@3.3.4)
+ '@vee-validate/zod':
+ specifier: 4.9.6
+ version: 4.9.6(vue@3.3.4)
+ '@vue/repl':
+ specifier: ^2.1.4
+ version: 2.1.4
'@vueuse/core':
specifier: ^10.2.1
version: 10.2.1(vue@3.3.4)
@@ -176,6 +188,9 @@ importers:
jsdom:
specifier: ^22.1.0
version: 22.1.0
+ lodash-es:
+ specifier: ^4.17.21
+ version: 4.17.21
remark-gfm:
specifier: ^3.0.1
version: 3.0.1
@@ -188,9 +203,18 @@ importers:
unist-util-visit:
specifier: ^4.1.2
version: 4.1.2
+ vee-validate:
+ specifier: ^4.9.6
+ version: link:../packages/vee-validate
vue:
specifier: ^3.3.0
version: 3.3.4
+ yup:
+ specifier: ^1.2.0
+ version: 1.2.0
+ zod:
+ specifier: ^3.21.4
+ version: 3.21.4
packages/i18n: {}
@@ -2460,6 +2484,16 @@ packages:
'@types/node': 18.0.3
dev: false
+ /@types/lodash-es@4.17.7:
+ resolution: {integrity: sha512-z0ptr6UI10VlU6l5MYhGwS4mC8DZyYer2mCoyysZtSF7p26zOX8UpbrV0YpNYLGS8K4PUFIyEr62IMFFjveSiQ==}
+ dependencies:
+ '@types/lodash': 4.14.195
+ dev: false
+
+ /@types/lodash@4.14.195:
+ resolution: {integrity: sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==}
+ dev: false
+
/@types/mdast@3.0.10:
resolution: {integrity: sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==}
dependencies:
@@ -2806,6 +2840,26 @@ packages:
vue: 3.3.4
dev: true
+ /@vee-validate/yup@4.9.6(vue@3.3.4):
+ resolution: {integrity: sha512-Gsd9V3IK4Tqxc2HzmCizHbjh/28uwhD6XAZeRd0cO86op8x05MXljGqAtcaghqjAgZhxLOyU7RRqrICPUzpWDw==}
+ dependencies:
+ type-fest: 3.12.0
+ vee-validate: 4.10.2(vue@3.3.4)
+ yup: 1.2.0
+ transitivePeerDependencies:
+ - vue
+ dev: false
+
+ /@vee-validate/zod@4.9.6(vue@3.3.4):
+ resolution: {integrity: sha512-YFpJDtsFDdM47HQSGKRQvA5nxEYZW1RjTWe1MPSpgo8uEWBOtxEwgiiPWoxnQtwc2c8qYpYMIwUbm2mUAHhpaw==}
+ dependencies:
+ type-fest: 3.12.0
+ vee-validate: 4.10.2(vue@3.3.4)
+ zod: 3.21.4
+ transitivePeerDependencies:
+ - vue
+ dev: false
+
/@vercel/nft@0.22.6:
resolution: {integrity: sha512-gTsFnnT4mGxodr4AUlW3/urY+8JKKB452LwF3m477RFUJTAaDmcz2JqFuInzvdybYIeyIv1sSONEJxsxnbQ5JQ==}
engines: {node: '>=14'}
@@ -3016,6 +3070,10 @@ packages:
dependencies:
'@vue/shared': 3.3.4
+ /@vue/repl@2.1.4:
+ resolution: {integrity: sha512-1fK5bsdM3vAkP6l5r0mQwGELaz2BWyk9vWDjA0h1RQ1+2qmf3YVDaJ7Pd7jW3RCaVZUwrIuHLfzRnu2p+ZjZMw==}
+ dev: false
+
/@vue/runtime-core@3.3.4:
resolution: {integrity: sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==}
dependencies:
@@ -6664,6 +6722,10 @@ packages:
dependencies:
p-locate: 5.0.0
+ /lodash-es@4.17.21:
+ resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+ dev: false
+
/lodash.camelcase@4.3.0:
resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
dev: true
@@ -10827,6 +10889,16 @@ packages:
spdx-expression-parse: 3.0.1
dev: true
+ /vee-validate@4.10.2(vue@3.3.4):
+ resolution: {integrity: sha512-J2we1IjIvEKSC32ECw1HWgwwyqDIsrUnhGhZWgrvHeNp6m+pHY5C6PrdSDy1Ly4NcDNNO5XGMNvRZnurIKIdEg==}
+ peerDependencies:
+ vue: ^3.3.4
+ dependencies:
+ '@vue/devtools-api': 6.5.0
+ type-fest: 3.12.0
+ vue: 3.3.4
+ dev: false
+
/vfile-location@4.1.0:
resolution: {integrity: sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==}
dependencies: