---
title: "Form"
description: "A form container with field layout and validation."
category: Form
status: beta
since: 0.3.4
tags: [form, validation, input]
syncStatus: reviewed
verified: true
---

## Usage

### Validation
`rules` match fields by `prop`, and `validate()` resolves to whether all of them pass.
:::TuffDemoWrapper{demo="FormFormDemo" code-lang="vue"}
---
code: |
  <script setup lang="ts">
  import { reactive, ref } from 'vue'

  const formRef = ref()
  const form = reactive({ name: '', email: '' })
  const rules = {
    name: { required: true, message: 'Please enter your name' },
    email: {
      validator: (value: string) => {
        if (!value) return 'Please enter your email'
        return value.includes('@') || 'Invalid email format'
      },
    },
  }

  async function submit() {
    if (!await formRef.value?.validate()) return
    // submit form
  }
  </script>

  <template>
    <TxForm ref="formRef" :model="form" :rules="rules" label-width="90px">
      <TxFormItem label="Name" prop="name">
        <TuffInput v-model="form.name" placeholder="Enter your name" />
      </TxFormItem>
      <TxFormItem label="Email" prop="email">
        <TuffInput v-model="form.email" placeholder="name@example.com" />
      </TxFormItem>
      <TxButton variant="primary" @click="submit">Validate</TxButton>
    </TxForm>
  </template>
---
:::

### Linking Label and Error
Spread the default slot's props onto the control so the label and error message point at it.

```vue
<TxFormItem v-slot="{ id, ariaInvalid, ariaDescribedby }" label="Email" prop="email">
  <input v-model="form.email" :id="id" :aria-invalid="ariaInvalid" :aria-describedby="ariaDescribedby">
</TxFormItem>
```

### Best Practices

- Keep `model` stable and reactive; replacing it after mount makes resets surprising.
- Put shared rules in form-level `rules`; use item-level `rules` only for one-off overrides.
- Run async availability checks in `validator` and return a precise error string.
- When the whole form is locked, pass `disabled` to `TxForm` and to every control.
- Call `clearValidate()` when switching records without resetting user input.

## API Reference

### TxForm

#### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `model` | `Record<string, any>` | - | Reactive form data; `TxFormItem` reads fields by `prop`. |
| `rules` | `FormRules` | - | Form-level rules keyed by `prop`. |
| `labelPosition` | `'left' \| 'right' \| 'top'` | `'left'` | Label layout of the child items. |
| `labelWidth` | `string \| number` | - | Label width outside the `top` layout; numbers are pixels. |
| `size` | `'small' \| 'medium' \| 'large'` | `'medium'` | Only written to the form context; inputs don't read it, so pass `size` per control. |
| `disabled` | `boolean` | `false` | Only written to the form context; inputs don't read it, so pass `disabled` per control. |

#### Events

| Event | Payload | Description |
|------|---------|-------------|
| `validate` | `(valid: boolean)` | Fires after `validate()` has checked every field. |

#### Slots

| Slot | Props | Description |
|------|------|-------------|
| `default` | - | Form items and actions. |

#### Exposed Methods

| Name | Type | Description |
|------|-----------|-------------|
| `validate` | `() => Promise<boolean>` | Validates every registered field and resolves to whether all passed. |
| `resetFields` | `() => void` | Restores each field's value at mount and clears messages. |
| `clearValidate` | `() => void` | Clears messages without changing `model`. |

### TxFormItem

#### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `label` | `string` | `''` | Label text; falls back to `prop`. |
| `prop` | `string` | - | The field's key in `model` and `rules`. |
| `rules` | `FormRule \| FormRule[]` | - | Rules for this field; override the form-level rules. |
| `required` | `boolean` | `false` | Shows the required marker and rejects empty values. |
| `showMessage` | `boolean` | `true` | Shows the error message below the field. |
| `inline` | `boolean` | `false` | Aligns label and content for compact inline rows. |

#### Events

| Event | Payload | Description |
|------|---------|-------------|
| `validate` | `(valid: boolean)` | Fires after this field validates. |

#### Slots

| Slot | Props | Description |
|------|------|-------------|
| `default` | `{ id, ariaInvalid, ariaDescribedby }` | The field control; spread the props to link the label and error message. |

### Types

`FormRule`, one entry of `rules`:

| Field | Type | Description |
|------|------|-------------|
| `required` | `boolean` | Rejects empty values. |
| `message` | `string` | Message when required fails or the validator returns `false`. |
| `validator` | `(value, rule, model) => boolean \| string \| Promise<boolean \| string>` | Custom check; a returned string becomes the error message. |

## Overview

- `TxForm` renders a native `<form>` and prevents the default submit.
- `TxFormItem` registers on mount and unregisters before unmount, so the form methods only touch live fields.
- Empty means `null`, `undefined`, `''`, or an empty array.
- The label's `for` targets a generated field id and the error message is `role="alert"`; the link is inert until the control takes the slot's `id`.

## Technologies

- Source: `packages/tuffex/packages/components/src/form/`.

<TuffDocSourceLink />
