---
title: "Composables"
description: "useForm, useField and useFieldArray, auto-imported from Formisch."
canonical_url: "https://nuxt-formisch.onmax.me/forms/composables"
---
# Composables

> useForm, useField and useFieldArray, auto-imported from Formisch.

The three Formisch composables are auto-imported. They are the same functions as in [`@formisch/vue`](https://formisch.dev/vue/api/), so Formisch's own guides apply as written.

## useForm

Creates the form store from a Valibot object schema.

```ts
const form = useForm({
  schema: ProfileSchema,
  initialInput: { name: 'Ada' },
  validate: 'submit', // default
  revalidate: 'input', // default
})
```

<table>
<thead>
  <tr>
    <th>
      Option
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        schema
      </code>
    </td>
    
    <td>
      Valibot object schema. Required.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        initialInput
      </code>
    </td>
    
    <td>
      Starting values. Fields without one start empty (<code>
        ''
      </code>
      
       for strings).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        emptyInput
      </code>
    </td>
    
    <td>
      The value a field gets when it has no input, per schema type, for example <code>
        { string: undefined }
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        validate
      </code>
    </td>
    
    <td>
      When to validate first: <code>
        'initial'
      </code>
      
      , <code>
        'touch'
      </code>
      
      , <code>
        'input'
      </code>
      
      , <code>
        'change'
      </code>
      
      , <code>
        'blur'
      </code>
      
       or <code>
        'submit'
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        revalidate
      </code>
    </td>
    
    <td>
      When to validate again after that. Same modes, except <code>
        'initial'
      </code>
      
      .
    </td>
  </tr>
</tbody>
</table>

The store is reactive. Read its state in templates and computed values:

<table>
<thead>
  <tr>
    <th>
      Property
    </th>
    
    <th>
      Meaning
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        isSubmitting
      </code>
    </td>
    
    <td>
      The submit handler is running.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isSubmitted
      </code>
    </td>
    
    <td>
      The form was submitted at least once.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isValidating
      </code>
    </td>
    
    <td>
      Validation is running.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isTouched
      </code>
      
       / <code>
        isEdited
      </code>
      
       / <code>
        isDirty
      </code>
    </td>
    
    <td>
      A field was focused / changed / differs from its initial input.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isValid
      </code>
    </td>
    
    <td>
      No field has an error.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        errors
      </code>
    </td>
    
    <td>
      Errors on the root object only. Use <a href="https://nuxt-formisch.onmax.me/forms/methods">
        <code>
          getDeepErrors
        </code>
      </a>
      
       for every field.
    </td>
  </tr>
</tbody>
</table>

## useField

Connects one field. Use it inside an input component that renders a single field; for several fields in one template, [`<FField>`](https://nuxt-formisch.onmax.me/forms/components#ffield) is simpler.

```vue [app/components/EmailInput.vue]
<script setup lang="ts">
import type { FormStore } from '@formisch/vue'

const props = defineProps<{ form: FormStore<typeof LoginSchema> }>()

const field = useField(() => props.form, { path: ['email'] })
</script>

<template>
  <label>
    Email
    <input v-model="field.input" v-bind="field.props" type="email">
  </label>
  <p v-if="field.errors">{{ field.errors[0] }}</p>
</template>
```

Both arguments accept a value, a ref or a getter. Pass a getter when the form or path comes from props, so the field follows them.

The field store has `input`, `errors`, `props`, `path`, `isTouched`, `isEdited`, `isDirty` and `isValid`. Paths are arrays: `['user', 'email']` for nested objects, `['todos', 0, 'label']` for array items.

## useFieldArray

Tracks a list. `items` holds a stable key per entry, so `v-for` keeps each row's state when you insert, move or remove.

```vue [app/components/TodoList.vue]
<script setup lang="ts">
import * as v from 'valibot'

const TodoSchema = v.object({
  todos: v.array(v.object({ label: v.pipe(v.string(), v.nonEmpty()) })),
})

const form = useForm({ schema: TodoSchema, initialInput: { todos: [{ label: '' }] } })
const todos = useFieldArray(form, { path: ['todos'] })
</script>

<template>
  <FForm :of="form" @submit="console.log">
    <div v-for="(key, index) in todos.items" :key="key">
      <FField v-slot="field" :of="form" :path="['todos', index, 'label']">
        <input v-model="field.input" v-bind="field.props">
      </FField>
      <button type="button" @click="remove(form, { path: ['todos'], at: index })">
        Remove
      </button>
    </div>

    <button type="button" @click="insert(form, { path: ['todos'], initialInput: { label: '' } })">
      Add
    </button>
  </FForm>
</template>
```

The array store has `items`, `errors`, `path`, `isTouched`, `isEdited`, `isDirty` and `isValid`. Change the array with the [array methods](https://nuxt-formisch.onmax.me/forms/methods#arrays).

## With Nuxt UI

Field stores bind straight to Nuxt UI inputs: `v-model` to `input`, and the first error to `UFormField`.

```vue [app/pages/profile.vue]
<script setup lang="ts">
const form = useForm({ schema: ProfileSchema })
const name = useField(form, { path: ['name'] })
const email = useField(form, { path: ['email'] })
</script>

<template>
  <FForm :of="form" class="space-y-4" @submit="console.log">
    <UFormField label="Name" :error="name.errors?.[0]">
      <UInput v-model="name.input" />
    </UFormField>
    <UFormField label="Email" :error="email.errors?.[0]">
      <UInput v-model="email.input" type="email" />
    </UFormField>
    <UButton type="submit" :loading="form.isSubmitting">Save</UButton>
  </FForm>
</template>
```

To render every field from the schema without writing them out, use [`<FAutoForm>`](https://nuxt-formisch.onmax.me/auto-form).


## Sitemap

See the full [sitemap](https://nuxt-formisch.onmax.me/sitemap.md) for all pages.
