GitHub

NotField

Renderless field wrapper. Exposes per-field state and event handlers through its default slot.

<NotField> tracks validation, dirty state, and touched state for a single field. It renders nothing itself — everything is handed to the slot.

Props

PropTypeRequiredDescription
pathstringYesDot-separated path to this field within form.values. Supports nested paths (address.city) and array item paths (tags.0).
formNotFormInstance<any>NoExplicit instance override. Takes priority over any <NotForm> ancestor. Required when used outside of <NotForm>.
validateOnPartial<Record<ValidationTrigger, boolean>>NoPer-field overrides for validation triggers. Merged over the form-level config — only the keys you specify are changed.
debouncenumberNoDebounce delay in milliseconds for input- and change-triggered validation.

Slot props

PropTypeDescription
pathstringThe path passed to this field.
valueanyRead-only snapshot of the current field value. Use v-model="form.values.fieldName" for binding — not this prop.
errorsStandardSchemaV1.Issue[]All validation issues for this field from the last validation run.
isValidbooleantrue when this field has no errors.
isTouchedbooleantrue after the user has blurred this field, or after the form has been submitted.
isDirtybooleantrue when the current value differs from the initial value.
isValidatingbooleantrue while an async validator is running for this field.
validate() => Promise<StandardSchemaV1.Result>Manually triggers field validation. Useful for custom inputs that manage their own interaction events.
events{ onBlur, onInput, onChange, onFocus }Native DOM event handlers. Spread onto native inputs or bind individually to custom components.

Usage

Spread events

The most common pattern. Spread all handlers at once onto a native input.

Bind individual events

When your component uses different event names, bind handlers individually.

<template>
  <NotField
    v-slot="{ events }"
    path="country"
  >
    <label :for="path">
      Country
      <CustomSelect
        :id="path"
        v-model="form.values.country"
        @close="events.onBlur"
        @select="events.onChange"
      />
    </label>
  </NotField>
</template>

Nested paths

<template>
  <NotField
    v-slot="{ events, path }"
    path="address.city"
  >
    <label :for="path">
      City
      <input
        :id="path"
        v-model="form.values.address.city"
        v-bind="events"
      >
    </label>

    <NotMessage :path="path" />
  </NotField>
</template>

Per-field trigger override

Override only the keys you want to change. The rest inherit from the form config.

<template>
  <!-- Form has onInput disabled — this field overrides it -->
  <NotField
    v-slot="{ events }"
    path="username"
    :validate-on="{ onInput: true }"
  >
    <label :for="path">
      Username
      <input
        :id="path"
        v-model="form.values.username"
        v-bind="events"
      >
    </label>
  </NotField>
</template>
<template>
  <!-- Form has onBlur enabled — disable it for this checkbox -->
  <NotField
    v-slot="{ events }"
    path="tos"
    :validate-on="{ onBlur: false }"
  >
    <label :for="path">
      <input
        :id="path"
        v-model="form.values.tos"
        type="checkbox"
        v-bind="events"
      >
      Accept terms
    </label>
  </NotField>
</template>

Validate on mount

<template>
  <NotField
    v-slot="{ events }"
    path="email"
    :validate-on="{ onMount: true }"
  >
    <label :for="path">
      Email
      <input
        :id="path"
        v-model="form.values.email"
        v-bind="events"
      >
    </label>
  </NotField>
</template>

Debounced validation

Add a delay between user input and validation firing. Useful for expensive validators or async checks like username availability.

<template>
  <NotField
    v-slot="{ events, isValidating }"
    path="username"
    :debounce="300"
  >
    <label :for="path">
      Username
      <input
        :id="path"
        v-model="form.values.username"
        v-bind="events"
      >
      <span v-if="isValidating">Checking…</span>
    </label>
  </NotField>
</template>

Conditional UI from slot state

<template>
  <NotField
    v-slot="{ events, isTouched, errors, isDirty, isValidating, isValid }"
    path="password"
  >
    <label :for="path">
      Password
      <input
        :id="path"
        v-model="form.values.password"
        type="password"
        v-bind="events"
      >
    </label>

    <span v-if="isValidating">Checking…</span>

    <span
      v-else-if="isTouched && !isValid"
      class="text-error"
    >
      {{ errors[0].message }}
    </span>

    <span
      v-else-if="isDirty && isValid"
      class="text-success"
    >
      Looks good
    </span>
  </NotField>
</template>

Singleton — no <NotForm> ancestor

Pass :form directly when the field is used outside of a <NotForm>.

<template>
  <NotField
    v-slot="{ events }"
    :form="form"
    path="search"
  >
    <label :for="path">
      Search
      <input
        :id="path"
        v-model="form.values.search"
        v-bind="events"
      >
    </label>
  </NotField>
</template>