Skip to content

Field ​

Experimental

A component that provides labeling and validation for form controls.

vue
<script setup lang="ts">
import { z } from 'zod'
import { useHField, HTextInput } from '@holistics/design-system'

const { value: emailValue, HField } = useHField<string>({
  path: 'email',
  rules: z.email(),
  opts: { initialValue: '[email protected]' },
})
</script>

<template>
  <div>
    <HTextInput
      v-model="emailValue"
      top-text="Using the modelValue from the composable"
    />

    <hr class="my-4">

    <HField
      label="Using HField component"
      label-position="top"
    >
      <template #default="{ fieldProps }">
        <HTextInput
          v-bind="fieldProps"
          placeholder="Enter your email"
        />
      </template>
    </HField>
    <p class="mt-5">
      Input value: {{ emailValue }}
    </p>
  </div>
</template>

Field control props mapping ​

html
<HField
  v-slot="{ fieldProps }"
>
  <HTextInput v-bind="fieldProps" />
</HField>

The fieldProps object provided by HField maps standard component props to the form state:

  • modelValue: The current value of the field.
  • onUpdate:modelValue: Updates the field value.
  • onBlur: Marks the field as touched and triggers validation (if mode is onBlur).
  • name: The field name.
  • id: A unique ID for accessibility.

Examples ​

Label position ​

By default, HField will follow the label-position prop of the parent HForm. You can override this behavior by passing the label-position prop to the HField component.

vue
<script setup lang="ts">
import { useHForm, HTextInput } from '@holistics/design-system'
import { z } from 'zod'

const schema = z.object({
  email: z.email(),
  password: z.string().min(8),
})

const { HForm, HField } = useHForm({
  validationSchema: schema,
})
</script>

<template>
  <HForm
    label-position="left"
  >
    <HField
      v-slot="{ fieldProps }"
      label="Email"
      name="email"
    >
      <HTextInput v-bind="fieldProps" />
    </HField>
    <HField
      v-slot="{ fieldProps }"
      label="Password"
      name="password"
      label-position="top"
    >
      <HTextInput v-bind="fieldProps" />
    </HField>
  </HForm>
</template>

With children slot ​

vue
<script setup lang="ts">
import { z } from 'zod'
import {
  useHField, HTextInput, HSelect, HTag,
} from '@holistics/design-system'

const { HField } = useHField({
  path: 'controls',
  rules: z.object({
    country: z.string(),
    postalCode: z.number(),
  }),
})
</script>

<template>
  <HField
    label="Controls"
    name="controls"
    necessity-indicator="never"
    label-position="left"
    class="w-full"
  >
    <template #default>
      <div class="flex gap-1">
        <HTag
          tag="Country"
        />
        <HTag
          tag="Postal Code"
        />
      </div>
    </template>
    <template #children>
      <div
        class="flex flex-col gap-2 rounded border bg-gray-100 p-4"
      >
        <HField
          v-slot="{ fieldProps }"
          label="Country"
          name="controls.country"
        >
          <HSelect
            v-bind="fieldProps"
            :options="[
              { label: 'US', value: 'US' },
              { label: 'VN', value: 'VN' },
            ]"
          />
        </HField>
        <HField
          v-slot="{ fieldProps }"
          label="Postal Code"
          name="controls.postalCode"
        >
          <HTextInput
            v-bind="fieldProps"
            type="number"
          />
        </HField>
      </div>
    </template>
  </HField>
</template>

Custom error message ​

The easiest way to customize error messages is directly within your Zod schema definition. vee-validate will pick these up and HField will display them automatically.

typescript
const schema = z.object({
  email: z.string().email({ message: "Please provide a valid work email" })
})

Async validation ​

You can perform asynchronous validation (e.g. checking if a username exists) using Zod's .refine() with an async function.

typescript
const schema = z.object({
  username: z.string().refine(async (name) => {
    const isTaken = await checkAvailability(name)
    return !isTaken
  }, { message: "Username is already taken" })
})

The form's isValidating state will be true while the async check is pending.

API ​

Pass-through: <div> ​

What does this mean?

All props, events, and attrs that are not specified in the tables below will be passed to the element/component described above.

Props ​

NameTypeDescription
name *
string

The name of the field for form validation and binding

disabled 
boolean

Whether the field is disabled

validationMode 
FormFieldValidationMode

Validation mode for the field

label 
string

The label text to display for the field

errorLabel 
string

The label text to display for the field errors

description 
string

Description for the field

labelPosition 
FormFieldLabelPosition

Position of the label relative to the input

labelAlign 
FormFieldLabelAlign

Alignment of the label text

tooltip 
string | (Omit<PopperProps, "trigger"> & Partial<Pick<PopperProps, "trigger">> & { content?: string; html?: boolean; shortcuts?: string[]; } & Partial<...> & Record<...>)

Tooltip for the field

necessityIndicator 
"asterisk" | "never"

Whether to show the necessary indicator

leftIcon 
"function" | "cancel" | "copy" | "cut" | "error" | "pause" | "play" | "add-block" | "add-circle" | "add-filter" | "add-tag" | "add-user" | "add" | "address-card" | "adhoc-query" | ... 466 more ...

Left icon for the field

aria-describedby 
string

The id of the element that describes the field

Slots ​

NameScopedDescription
#default
HFieldSlotProps<any>
#children
HFieldSlotProps<any>
#additional-controls
HFieldSlotProps<any>

useHField composable ​

Parameters ​

NameTypeDescription
path *MaybeRefOrGetter<string>The unique path/name for the field instance.
rules ZodTypeZod schema definition for field validation.
opts FieldOptions & { validationMode?: FormFieldValidationMode }Configuration options including initial value and validation mode.

Return ​

NameTypeDescription
HField ComponentHeadless field wrapper component that provides layout and label support.
value Ref<TValue>The current value of the field.
meta FieldMeta<TValue>Metadata about the field state (valid, dirty, touched, etc).
errors Ref<string[]>Array of error messages for the field.
errorMessage Ref<string | undefined>The first error message of the field.
handleBlur (e?: any) => voidHandler for blur events.
handleChange (e: any) => voidHandler for change events.
setValue (value: TValue) => voidUpdates the field value.
validate () => Promise<ValidationResult>Triggers validation for the field.
resetField (state?: Partial<FieldState>) => voidResets the field state.