---
layout: ../../../layouts/PageLayout.astro
title: Nested Objects and Arrays
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 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:

```js
const { defineInputBinds } = useForm();

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:

```js
{
  "links": {
    "twitter": "https://twitter.com/logaretm",
    "github": "https://github.com/logaretm"
  }
}
```

Here is a live example so you can see it in action.

<Repl files={{ 'App.vue': 'CompositionNested01' }} client:visible />

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.

```js
const { defineInputBinds } = useForm();

const twitterLink = defineInputBinds('links[0]');
const githubLink = defineInputBinds('links[1]');
```

Here is the same example as above but in array format:

<Repl files={{ 'App.vue': 'CompositionNested02' }} client:visible />

Submitting the previous form would result in the following values being passed to your handler:

```js
{
  "links": [
    "https://twitter.com/logaretm",
    "https://github.com/logaretm"
  ]
}
```

<DocTip type="warn">

vee-validate will only create nested arrays if the path expression is a complete number, for example, paths like `some.nested[0path]` will not create any arrays because the `0path` key is not a number. However `some.nested[0].path` will create the array with an object as the first item.

</DocTip>

## Avoiding Nesting

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.

```js
const { defineInputBinds } = useForm();

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:

```js
{
  "links.twitter": "https://twitter.com/logaretm",
  "links.github": "https://github.com/logaretm"
}
```

Here is a live example on that:

<Repl files={{ 'App.vue': 'CompositionNested03' }} client:visible />

## 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".

To set up a repeatable block of the form, you can use `useFieldArray` to help you manage the array values and operations. It requires a form context to be present so `useForm` must be called before you use `useFieldArray` either in parent component or in the same component.

```js
import { useForm, useFieldArray } from 'vee-validate';

useForm();

const { remove, push, fields } = useFieldArray('users');
```

The `useFieldArray` exposes a few properties and functions to help you manage the array fields, the most special one is the `fields` array which contains the array items and represents their current value in the form. You will use `fields` as an iteration source to render the array items.

Here is an example of a repeatable block of fields, we mutate the field value with `v-model` directly on the iteration item value.

<Repl files={{ 'App.vue': 'CompositionNested04' }} client:visible />

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.

<DocTip type="warn">

The `key` property generated on array items is not an index. It is a unique identifier for the array item that is independent of the array index, so you should not be using it to reference field names.

</DocTip>

### Field 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:

_*Iterate over the `users` array:*_

```js
const { remove, push, fields } = useFieldArray('users');
```

_*Iterate over the `domains` inside `settings.dns` object:*_

```js
const { remove, push, fields } = useFieldArray('settings.dns.domains');
```

### Array Helpers

The `<useFieldArray />` function provides the following properties and functions:

- `fields`: a **read-only** version of your array field items, it includes some useful properties like `key`, `isFirst` and `isLast`, the actual item value is inside `.value` property. You should use it to iterate with `v-for`.
- `push(item: any)`: adds an item to the end of the array.
- `prepend(item: any)`: adds an item to the start of the array.
- `insert(idx: number, item: any)`: Inserts an array item at the specified index.
- `remove(idx: number)`: removes the item with the given index from the array.
- `swap(idxA: number, idxB: number)`: Swaps two array elements by their indexes.
- `replace(items: any[])`: Replaces the entire array values with the given items.
- `update(idx: number, value: any)`: Updates an array item value at the specified index.
- `move(oldIdx: number, newIdx: number)`: Moves an array item to a different position within the array.

[Read the API reference](/api/use-field-array) for more information.

## Caveats

### Paths creation and destruction

vee-validate creates the paths inside the form data automatically but lazily, so initially, your form values won't contain the values of the fields unless you provide initial values for them. It might be worthwhile to provide initial data for your forms with nested paths.

When fields get unmounted like in the case of conditional rendered fields with `v-if` or `v-for`, their path will be destroyed just as it was created if they are the last field in that path. So you need to be careful while accessing the nested field in `values` inside your submission handler.

Path destruction can be annoying when dealing with multi-step forms or tabbed forms where you want all the values to be available even when the fields are unmounted. You can control this behavior by passing `keepValueOnUnmount` prop to the `useField` function or you can do it for all the fields by passing `keepValuesOnUnmount` to the `useForm` function.

Note that the priority of this configuration follows the field config first then it fallbacks to the form's config.

```js
import { useForm } from 'vee-validate';

// keep all values when their fields get unmounted
const { values } = useForm({
  keepValuesOnUnmount: true,
});
```

```js
import { useField } from 'vee-validate';

// this field value will be removed
const field = useField('field', undefined, {
  keepValueOnUnmount: false,
});
```

### Referencing Errors

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.

Since vee-validate supports `yup` and `zod` schemas, 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 an example:

<Repl files={{ 'App.vue': 'CompositionNested05' }} client:visible />

You can [visit this link](/examples/array-fields) for a practical example using nested arrays.
