Components

Form Field

Source
Label, description, hint, help, and error scaffold around a single form control.
Install with the CLI
npx @vyui/cli add form-field

Overview

VyFormField wraps the headless @vyui/core FormField with the text scaffold around a control: label, description, hint, help, and error. Inside a VyForm it registers itself under name, runs its validators, and renders the resulting error; on its own it still renders the scaffold, but the value and error are yours to supply.

Usage

The default slot receives the field's live value, error, setValue, and disabled. Kit controls do not read the field context on their own, so bind them through the slot.

<script setup lang="ts">
import { VyForm } from '@vyui/kit/form'
import { VyFormField } from '@vyui/kit/form-field'
import { VyInput } from '@vyui/kit/input'

const required = (value: unknown) => (value ? null : 'Email is required')
</script>

<template>
  <VyForm :default-values="{ email: '' }">
    <VyFormField
      name="email"
      label="Email"
      description="We only use this to send receipts."
      help="Work addresses are fine."
      required
      :validators="[required]"
    >
      <template #default="{ value, setValue }">
        <VyInput
          :model-value="value as string"
          placeholder="you@example.com"
          @update:model-value="setValue"
        />
      </template>
    </VyFormField>
  </VyForm>
</template>

Passing only name to the control forwards the name to the native input; it does not connect the control to the form. Use setValue from the slot.

Manual error

error overrides whatever the surrounding form derived, for server-side validation or a control that validates itself.

<template>
  <VyFormField name="code" label="Invite code" :error="serverError">
    <template #default="{ value, setValue }">
      <VyPinInput :model-value="value as string" @update:model-value="setValue" />
    </template>
  </VyFormField>
</template>

Slot overrides

label, description, hint, help, and error each take a slot when text alone is not enough. The error slot receives the resolved message.

Features and behavior

  • name must be unique inside the parent form and is treated as static — swapping it at runtime orphans the registration.
  • validators run synchronously on submit and stop at the first non-null message.
  • defaultValue seeds the field only when the form's defaultValues has no entry for name.
  • The error text replaces help while it is showing; the two never render together.
  • required appends a red asterisk to the label, and is presentational — pair it with a validator to actually enforce the field.
  • Label and hint share one row; description sits under them, and both blocks are omitted when their props and slots are empty.
  • Outside a VyForm the component throws, because the core primitive injects the form root context.

API

Props

PropDefaultType
defaultValueunknown

Initial value when the form's `defaultValues` doesn't supply one.

descriptionstring | undefined

Description rendered between the label and the control. Overridden by the `description` slot.

errorstring | undefined

Manual error override. When provided, takes precedence over the validator-derived error from the surrounding `<VyForm>`.

helpstring | undefined

Helper text rendered below the control. Hidden when an error is showing.

hintstring | undefined

Small auxiliary text rendered next to the label (right-aligned). Overridden by the `hint` slot.

labelstring | undefined

Label text rendered above the control. Overridden by the `label` slot.

name*string

Field name — must be unique within the parent `<VyForm>`.

requiredfalseboolean | undefined

Marks the field as required — appends a red asterisk to the label.

size"md" | "sm" | "lg" | "xl" | undefined
uiPartial<Record<"root" | "label" | "description" | "error" | "wrapper" | "hint" | "help" | "labelWrapper" | "container", ClassNameValue>> | undefined
validatorsFormFieldValidator[] | undefined

Synchronous validators (see `@vyui/core`). Stops at the first error.

Slots

SlotBindings
default{ value: unknown; error: string | null; setValue: (value: unknown) => void; disabled: boolean; }
label{} | undefined
description{} | undefined
hint{} | undefined
help{} | undefined
error{ error: string; }

Styling and theming

Override globally through appConfig.ui.formField or per instance with ui.

UI slotPurpose
rootVertical stack for the whole field.
wrapperLabel, hint, and description block.
labelWrapperRow holding the label and the right-aligned hint.
labelLabel text, plus the required asterisk.
descriptionText between the label and the control.
containerWrapper around the control slot.
errorError message under the control.
hintAuxiliary text next to the label.
helpHelper text under the control.

size scales the label, description, hint, help, and error text from sm through xl; it does not size the control itself.

Accessibility

The scaffold is text next to a control, not a native label association — Lynx has no for/id pairing. Screen readers announce the control by its own accessibility-label, so give icon-only or ambiguous controls one that matches the visible label, and repeat the error message there when a field is invalid.

  • Form collects the values these fields register.
  • Label for a standalone label with no validation scaffold.
  • Input, Textarea, and Select for controls to place inside a field.