# NotField

> Renderless component for individual form fields with validation state and event handlers.

`<NotField>` is a renderless component that connects an individual field to NotForm's validation and state management.

It provides the field's current state, validation errors, and event handlers that can be bound to native inputs or custom form components.

## Usage

A typical field combines the slot props with a form input and `<NotMessage>`.

<code-preview>
<demos-native-not-field>



</demos-native-not-field>

<template v-slot:code="">
<code-collapse>
<docs-demo-code file="native/not-field">



</docs-demo-code>
</code-collapse>
</template>
</code-preview>

## API

### Props

<field-group>
<field name="path" type="Paths<TSchema>" :required="true">

Dot path of this field. It identifies the value managed by the field.

<br />

<br />



Fields on a top-level property use a single property name:

```vue
<template>
  <NotField path="email">
    ...
  </NotField>
</template>
```

Nested fields use dot notation:

```vue
<template>
  <NotField path="user.email">
    ...
  </NotField>
</template>
```

The same path is exposed through the default slot and can be used for labels, IDs, messages, and other field-specific elements.

</field>

<field name="form" type="NotFormAPI<TSchema>">

Form instance. Overrides `<NotForm>` inject.

<br />

<br />



By default, `<NotField>` uses the form instance provided by a surrounding `<NotForm>`.

<br />

<br />



You can explicitly provide a form instance when needed:

```vue
<template>
  <NotField
    v-slot="{ events }"
    :form="form"
    path="email"
  >
    <input v-bind="events">
  </NotField>
</template>
```

An explicit `form` takes priority over the form provided by a `<NotForm>` ancestor.

<br />

<br />



This is also required when using `<NotField>` as a standalone field outside of a `<NotForm>`.

</field>

<field name="validateOn" type="Partial<Record<ValidationTrigger, boolean>>">

Validation triggers merged over defaults: `{ onBlur: true, onChange: true }`.

<br />

<br />



This allows individual validation triggers to be overridden for a field.

<br />

<br />



Only the triggers you specify are overridden; all other trigger settings remain unchanged.

```vue
<template>
  <NotField
    path="username"
    :validate-on="{ onInput: true }"
  />
</template>
```

For example, in addition to the defaults, the field above will additionally validate on input.

<br />

<br />



`onMount` is also a valid trigger, even though it has no corresponding entry in `events` below — there's no DOM event to bind for "the field just appeared," so instead it's opted into here and runs automatically once, when the field is created. This is useful for surfacing validation issues on a prefilled value without waiting for the user to interact with the field:

```vue
<template>
  <NotField
    path="promoCode"
    :validate-on="{ onMount: true }"
  />
</template>
```

</field>

<field name="validationMode" type="'eager' | 'lazy'">

Controls how aggressively a field revalidates after it's been touched: `lazy` waits for the next blur or submit, `eager` also revalidates as the user types.

<br />

<br />



Defaults to `eager`.

<br />

<br />



In `eager` mode, once a field has an issue, it revalidates on every change/input event (subject to `validateOn` and `debounce`) so the error can clear as soon as the user fixes it. Until a field is invalid, change/input events are ignored — validation only starts once blur or submit produces the first issue.

<br />

<br />



In `lazy` mode, validation only runs on blur and submit, no matter how many issues are present. This produces less noise for fields where feedback on every keystroke isn't wanted.

<note>

`validateOn` and `validationMode` control different things: `validateOn` decides *which* interactions are allowed to trigger validation at all, while `validationMode` decides *how often* those allowed triggers actually fire once the field already has an issue. Setting `validateOn: { onInput: true }` with `validationMode="lazy"`, for instance, still won't revalidate on every keystroke — `lazy` only revalidates on blur or submit regardless of which triggers `validateOn` allows.

</note>

<code-preview>
<demos-native-validation-modes>



</demos-native-validation-modes>

<template v-slot:code="">
<code-collapse>
<docs-demo-code file="native/validation-modes">



</docs-demo-code>
</code-collapse>
</template>
</code-preview>
</field>

<field name="debounce" type="number">

Delays input- and change-triggered validation until the user stops interacting with the field for the specified number of milliseconds.

<br />

<br />



Omit or set to `0` to disable.

<br />

<br />



This is particularly useful for asynchronous validation, where validating on every keystroke could result in excessive requests.

```vue
<template>
  <NotField
    v-slot="{ events }"
    path="username"
    :debounce="400"
  >
    <input
      v-model="form.values.username"
      v-bind="events"
    >
  </NotField>
</template>
```

<note>

Blur and submit-triggered validation are always run immediately and are never delayed by `debounce`.

</note>
</field>
</field-group>

### Slots

#### `default`

The default slot receives the complete field state and event handlers.

```vue
<template>
  <NotField
    v-slot="slotProps"
    path="email"
  >
    ...
  </NotField>
</template>
```

<field-group>
<field name="path" type="string">

Dot path of this field.

<br />

<br />



Useful for labels, inputs, IDs, messages, and other field-specific elements.

```vue
<template>
  <NotField
    v-slot="{ path }"
    path="email"
  >
    <label :for="path">Email</label>

    <input :id="path">
  </NotField>
</template>
```

</field>

<field name="errors" type="Array<Issue>">

Validation issues for this field path.

<br />

<br />



It contains all validation issues reported for the field during the most recent validation run.

```vue
<template>
  <NotField
    v-slot="{ errors }"
    path="email"
  >
    <input v-model="form.values.email">

    <ul v-if="errors.length > 0">
      <li
        v-for="error in errors"
        :key="error.message"
      >
        {{ error.message }}
      </li>
    </ul>
  </NotField>
</template>
```

</field>

<field name="isValid" type="boolean">

Whether this field has no issues.

<br />

<br />



It is `true` if the field has no issues and `false` otherwise.

```vue
<template>
  <NotField
    v-slot="{ isValid }"
    path="email"
  >
    <span v-if="isValid">Valid</span>
  </NotField>
</template>
```

</field>

<field name="isTouched" type="boolean">

Whether the user has interacted with this field.

<br />

<br />



This can be useful for displaying validation feedback only after the user has interacted with the field:

```vue
<template>
  <NotField
    v-slot="{ isTouched, isValid, events }"
    path="email"
  >
    <input
      v-model="form.values.email"
      v-bind="events"
    >

    <p v-if="isTouched && !isValid">
      Please enter a valid email address.
    </p>
  </NotField>
</template>
```

</field>

<field name="isDirty" type="boolean">

Whether the value differs from the baseline.

```vue
<template>
  <NotField
    v-slot="{ isDirty }"
    path="email"
  >
    <span v-if="isDirty">Unsaved changes</span>
  </NotField>
</template>
```

</field>

<field name="isValidating" type="boolean">

Whether this field is currently validating.

```vue
<template>
  <NotField
    v-slot="{ isValidating }"
    path="username"
  >
    <span v-if="isValidating">
      Checking username...
    </span>
  </NotField>
</template>
```

</field>

<field name="events" type="Record<ExcludeStrict<ValidationTrigger, 'onMount'>, () => void>">

Handlers to bind to the input.

<br />

<br />



These handlers are used to track field interaction and trigger validation.

<br />

<br />



`onMount` is excluded here even though it's a valid `validateOn` trigger — see the note on `validateOn` above for why.

- **Binding all events:**

For native inputs, the simplest approach is to spread the handlers:

```vue
<template>
  <NotField
    v-slot="{ events, path }"
    path="email"
  >
    <label :for="path">Email</label>

    input
    :id="path"
    v-model="form.values.email"
    v-bind="events"
    type="email"
    >
  </NotField>
</template>
```

- **Binding individual events:**

For custom components, bind the relevant handlers individually:

```vue
<template>
  <NotField
    v-slot="{ events }"
    path="country"
  >
    <CustomCombobox
      @focusout="events.onBlur"
      @pick="events.onChange"
    />
  </NotField>
</template>
```

<note>

The handlers don't require the original DOM event as an argument. They simply notify `<NotField>` that the corresponding interaction occurred.

</note>
</field>
</field-group>
