---
title: "Components"
description: "FForm, FField and FFieldArray, Formisch's headless components with an F prefix."
canonical_url: "https://nuxt-formisch.onmax.me/forms/components"
---
# Components

> FForm, FField and FFieldArray, Formisch's headless components with an F prefix.

The module registers Formisch's three components under an `F` prefix ([configurable](https://nuxt-formisch.onmax.me/getting-started/configuration)). They render no markup of their own besides `<form>`, so you style the inputs.

## FForm

Wraps a native `<form>` with `novalidate`. On submit it prevents the page reload, validates, focuses the first invalid field if there is one, and otherwise calls `@submit` with the validated output.

```vue
<FForm :of="form" @submit="onSubmit">
  <!-- fields -->
</FForm>
```

<table>
<thead>
  <tr>
    <th>
      Prop / event
    </th>
    
    <th>
      Type
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        of
      </code>
    </td>
    
    <td>
      <code>
        FormStore
      </code>
      
       from <code>
        useForm
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        @submit
      </code>
    </td>
    
    <td>
      <code>
        (output, event) => void | Promise<void>
      </code>
      
      . Type it with <code>
        SubmitHandler<typeof Schema>
      </code>
      
      , or <code>
        SubmitEventHandler
      </code>
      
       when you need the event.
    </td>
  </tr>
</tbody>
</table>

`form.isSubmitting` is `true` while an async handler runs. If the handler throws, its message lands in `form.errors`, so a failed request can show up without extra state:

```vue
<p v-if="form.errors">{{ form.errors[0] }}</p>
```

## FField

Gives its slot the [field store](https://nuxt-formisch.onmax.me/forms/composables#usefield) for one path.

```vue
<FField v-slot="field" :of="form" :path="['user', 'email']">
  <input v-model="field.input" v-bind="field.props" type="email">
  <p v-if="field.errors">{{ field.errors[0] }}</p>
</FField>
```

<table>
<thead>
  <tr>
    <th>
      Prop
    </th>
    
    <th>
      Type
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        of
      </code>
    </td>
    
    <td>
      <code>
        FormStore
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        path
      </code>
    </td>
    
    <td>
      A valid path into the schema, checked by TypeScript.
    </td>
  </tr>
</tbody>
</table>

## FFieldArray

Gives its slot the [field array store](https://nuxt-formisch.onmax.me/forms/composables#usefieldarray). Key each row by its entry in `items`.

```vue
<FFieldArray v-slot="todos" :of="form" :path="['todos']">
  <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>
  <p v-if="todos.errors">{{ todos.errors[0] }}</p>
</FFieldArray>
```

## With Nuxt UI

Use Nuxt UI inputs inside `<FField>` like any other input. `UFormField` shows the label and the first error:

```vue
<FForm :of="form" class="space-y-4" @submit="onSubmit">
  <FField v-slot="field" :of="form" :path="['email']">
    <UFormField label="Email" :error="field.errors?.[0]">
      <UInput v-model="field.input" type="email" @blur="field.props.onBlur" />
    </UFormField>
  </FField>

  <FField v-slot="field" :of="form" :path="['role']">
    <UFormField label="Role" :error="field.errors?.[0]">
      <USelect v-model="field.input" :items="['developer', 'designer', 'manager']" />
    </UFormField>
  </FField>

  <UButton type="submit" :loading="form.isSubmitting">Save</UButton>
</FForm>
```


## Sitemap

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