---
title: Field
---
The QField component is used to provide common functionality and aspect to form components. It uses `:model-value` (or `v-model` if you want to use `clearable` property) to have knowledge of the model of the component inside. It has support for labels, hints, errors, validation, and comes in a variety of styles and colors.

QField allows you to display any form control (or almost anything as a matter of fact) inside it. Just place your desired content inside the `control` slot.

> [!WARNING]
> Do NOT wrap QInput, QFile or QSelect with QField as these components already inherit QField.

## QField API

### Props

- `model-value` (any, optional)
  Model of the component; Either use this property (along with a listener for 'update:model-value' event) OR use v-model directive
- `error` (boolean, optional), default `null`
  Does field have validation errors?; Setting it to a Boolean (even false) reserves the bottom space for the error message so the layout does not shift when the error appears; leave it at null (the default) when not using external validation, or use 'hide-bottom-space' to not reserve the space
- `error-message` (string, optional)
  Validation error message (gets displayed only if 'error' is set to 'true')
  Examples: `'Username must have at least 5 characters'`
- `no-error-icon` (boolean, optional)
  Hide error icon when there is an error
- `rules` (any[], optional)
  Array of Functions/Strings; If String, then it must be a name of one of the embedded validation rules
  Examples:
    - `[val => val.length <= 3 || 'Please use maximum 3 characters']`
    - `['fulltime']`
    - `[(val, rules) => rules.email(val) || 'Please enter a valid email address']`
- `reactive-rules` (boolean, optional)
  By default a change in the rules does not trigger a new validation until the model changes; If set to true then a change in the rules will trigger a validation; Has a performance penalty, so use it only when you really need it
- `lazy-rules` (boolean | string, optional), default `false`
  If set to boolean true then it checks validation status against the 'rules' only when field loses focus; while an error is displayed it re-checks on each model change so the error clears as soon as the value becomes valid; If set to 'ondemand' then it will trigger only when component's validate() method is manually called or when the wrapper QForm submits itself
  Accepts: `true`, `false`, `'ondemand'`
- `label` (string, optional)
  A text label that will “float” up above the input field, once the field gets focus
  Examples: `'Username'`
- `stack-label` (boolean, optional)
  Label will be always shown above the field regardless of field content (if any)
- `hint` (string, optional)
  Helper (hint) text which gets placed below your wrapped form component
  Examples: `'Fill in between 3 and 12 characters'`
- `hide-hint` (boolean, optional)
  Hide the helper (hint) text when field doesn't have focus
- `prefix` (string, optional)
  Prefix
  Examples: `'$'`
- `suffix` (string, optional)
  Suffix
  Examples: `'@gmail.com'`
- `label-color` (string, optional)
  Color name for the label from the Quasar Color Palette; Overrides the 'color' prop; The difference from 'color' prop is that the label will always have this color, even when field is not focused
  Examples: `'primary'`, `'teal'`, `'teal-10'`
- `color` (string, optional)
  Color name for component from the Quasar Color Palette
  Examples: `'primary'`, `'teal'`, `'teal-10'`
- `bg-color` (string, optional)
  Color name for component from the Quasar Color Palette
  Examples: `'primary'`, `'teal'`, `'teal-10'`
- `dark` (boolean, optional), default `null`
  Notify the component that the background is a dark color
- `loading` (boolean, optional)
  Signals the user a process is in progress by displaying a spinner; Spinner can be customized by using the 'loading' slot.
- `clearable` (boolean, optional)
  Appends clearable icon when a value (not undefined or null) is set; When clicked, model becomes null
- `clear-icon` (string, optional)
  Custom icon to use for the clear button when using along with 'clearable' prop
  Examples: `'close'`
- `filled` (boolean, optional)
  Use 'filled' design for the field
- `outlined` (boolean, optional)
  Use 'outlined' design for the field
- `borderless` (boolean, optional)
  Use 'borderless' design for the field
- `standout` (boolean | string, optional)
  Use 'standout' design for the field; Specifies classes to be applied when focused (overriding default ones); a text color class in there also colors the content and the decorators
  Examples: `true`, `'bg-primary text-white'`
- `label-slot` (boolean, optional)
  Enables label slot; You need to set it to force use of the 'label' slot if the 'label' prop is not set
- `bottom-slots` (boolean, optional)
  Enables bottom slots ('error', 'hint', 'counter')
- `hide-bottom-space` (boolean, optional)
  Do not reserve space for hint/error/counter anymore when these are not used; As a result, it also disables the animation for those
- `counter` (boolean, optional)
  Show an automatic counter on bottom right
- `rounded` (boolean, optional)
  Applies a small standard border-radius for a squared shape of the component
- `square` (boolean, optional)
  Remove border-radius so borders are squared; Overrides 'rounded' prop
- `dense` (boolean, optional)
  Dense mode; occupies less space
- `item-aligned` (boolean, optional)
  Match inner content alignment to that of QItem
- `disable` (boolean, optional)
  Put component in disabled mode
- `readonly` (boolean, optional)
  Put component in readonly mode
- `autofocus` (boolean, optional)
  Focus field on initial component render
- `for` (string, optional)
  Used to specify the 'id' of the control and also the 'for' attribute of the label that wraps it; If no 'name' prop is specified, then it is used for this attribute as well
  Examples: `'myFieldsId'`
- `maxlength` (string | number, optional)
  Specify a max length of model
- `tag` (string, optional), default `'label'` *(added v2.13.1)*
  HTML tag to use
  Examples: `'div'`, `'label'`

### Computed Props

- `hasError` (boolean, optional)
  Whether the component is in error state

### Methods

- `resetValidation(): void`
  Reset validation status
- `validate(value?: any): boolean | Promise<boolean>`
  Trigger a validation
  Params:
    - `value` (any, optional)
      Optional value to validate against
  Returns: `boolean | Promise<boolean>`
    True/false if no async rules, otherwise a Promise with the outcome (true -> validation was a success, false -> invalid models detected)
    Examples: `true`, `validate().then(outcome => { ... })`
- `focus(): void`
  Focus component
- `blur(): void`
  Blur component (lose focus)

### Events

- `@update:model-value`
  Emitted when the model changes, only when used with 'clearable' or the 'control' scoped slot.
  Params:
    - `value` (any, required)
      New model value
- `@focus`
  Emitted when component gets focused
  Params:
    - `evt` (Event, optional)
      JS event object
- `@blur`
  Emitted when component loses focus
  Params:
    - `evt` (Event, optional)
      JS event object
- `@clear`
  When using the 'clearable' property, this event is emitted when the clear icon is clicked
  Params:
    - `value` (any, optional)
      The previous value before clearing it

### Slots

- `#default`
  Field main content
- `#prepend`
  Prepend inner field; Suggestions: QIcon, QBtn
- `#append`
  Append to inner field; Suggestions: QIcon, QBtn
- `#before`
  Prepend outer field; Suggestions: QIcon, QBtn
- `#after`
  Append outer field; Suggestions: QIcon, QBtn
- `#label`
  Slot for label; Used only if 'label-slot' prop is set or the 'label' prop is set; When it is used the text in the 'label' prop is ignored
- `#error`
  Slot for errors; Enabled only if 'bottom-slots' prop is used; Suggestion: <div>
- `#hint`
  Slot for hint text; Enabled only if 'bottom-slots' prop is used; Suggestion: <div>
- `#counter`
  Slot for counter text; Enabled only if 'bottom-slots' prop is used; Suggestion: <div>
- `#loading`
  Override default spinner when component is in loading mode; Use in conjunction with 'loading' prop

### Scoped Slots

- `#control`
  Slot for controls; Suggestion QSlider, QRange, QKnob, ...
  Scope:
    - `id` (string, optional)
      Element id used in the 'for' attribute of the field label. Can be used to link the control to the label
      Examples: `'qf_363270c0-7a83-62b1-8dcf-6dfd64ee38fa'`
    - `field` (Element, optional)
      DOM element of the field
    - `editable` (boolean, optional)
      Field is editable
    - `focused` (boolean, optional)
      Field has focus
    - `floatingLabel` (boolean, optional)
      Field's label is floating
    - `modelValue` (any, optional)
      Field's value
      Examples: `0.241`, `'Text'`
    - `emitValue` (Function, optional)
      Function that emits an @input event in the context of the field
      Function signature: `(value: any) => void`
      Params:
        - `value` (any, required)
          Value to be emitted
          Examples: `0`, `'Changed text'`
    - `ariaInvalid` (string, optional) *(added v2.25)*
      Value for the control's 'aria-invalid' attribute when the field has an error
      Examples: `'true'`
    - `ariaDescribedby` (string, optional) *(added v2.25)*
      Element id for the field error message; Use it for the control's 'aria-describedby' attribute when rendering a custom control
      Examples: `'qf_363270c0-7a83-62b1-8dcf-6dfd64ee38fa_error'`
    - `ariaErrormessage` (string, optional) *(added v2.25)*
      Element id for the field error message; Use it for the control's 'aria-errormessage' attribute when rendering a custom control
      Examples: `'qf_363270c0-7a83-62b1-8dcf-6dfd64ee38fa_error'`

## Design

> [!NOTE]
> The examples below use dumb content (text) just to show you the design that QField can use. For checking out examples that wrap real components, see the "Basic Features" section.

> [!TIP]
> QField does not (and should not) manage your `control` slot, so if you use `label` prop, it might be a good idea to also specify `stack-label`, otherwise it might overlap your control when QField is not focused.

### Overview

For your QField you can use only one of the main designs (`filled`, `outlined`, `standout`, `borderless`). You cannot use multiple as they are self-exclusive.

Example "Design Overview":

```vue
<template>
  <div class="q-gutter-md" style="max-width: 300px">
    <q-field label="Standard" stack-label>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0"
          >Field content</div
        >
      </template>
    </q-field>

    <q-field filled label="Filled" stack-label>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0"
          >Field content</div
        >
      </template>
    </q-field>

    <q-field outlined label="Outlined" stack-label>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0"
          >Field content</div
        >
      </template>
    </q-field>

    <q-field standout label="Standout" stack-label>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0"
          >Field content</div
        >
      </template>
    </q-field>

    <q-field
      standout="bg-teal text-white"
      label="Custom standout"
      stack-label
    >
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0"
          >Field content</div
        >
      </template>
    </q-field>

    <q-field borderless label="Borderless" stack-label>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0"
          >Field content</div
        >
      </template>
    </q-field>

    <q-field rounded filled label="Rounded filled" stack-label>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0"
          >Field content</div
        >
      </template>
    </q-field>

    <q-field rounded outlined label="Rounded outlined" stack-label>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0"
          >Field content</div
        >
      </template>
    </q-field>

    <q-field rounded standout label="Rounded standout" stack-label>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0"
          >Field content</div
        >
      </template>
    </q-field>

    <q-field square filled label="Square filled" stack-label>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0"
          >Field content</div
        >
      </template>
    </q-field>

    <q-field square outlined label="Square outlined" stack-label>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0"
          >Field content</div
        >
      </template>
    </q-field>

    <q-field square standout label="Square standout" stack-label>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0"
          >Field content</div
        >
      </template>
    </q-field>
  </div>
</template>
```

### Coloring

```vue
<template>
  <div class="q-gutter-y-md column" style="max-width: 300px">
    <q-field color="purple-12" label="Label" stack-label>
      <template #prepend>
        <q-icon name="event" />
      </template>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field color="teal" filled label="Label" stack-label>
      <template #prepend>
        <q-icon name="event" />
      </template>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field
      color="grey-3"
      label-color="orange"
      outlined
      label="Label"
      stack-label
    >
      <template #append>
        <q-icon name="event" color="orange" />
      </template>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field
      color="lime-11"
      bg-color="green"
      filled
      label="Label"
      stack-label
    >
      <template #prepend>
        <q-icon name="event" />
      </template>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field color="teal" outlined label="Label" stack-label>
      <template #append>
        <q-avatar>
          <img
            alt="Quasar logo"
            src="https://cdn.quasar.dev/logo-v2/svg/logo.svg"
          />
        </q-avatar>
      </template>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field
      color="orange"
      standout
      bottom-slots
      :model-value="text"
      label="Label"
      stack-label
      counter
      clearable
    >
      <template #prepend>
        <q-icon name="place" />
      </template>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
      <template #append>
        <q-icon name="favorite" />
      </template>

      <template #hint> Field hint </template>
    </q-field>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const text = ref('Field content')
</script>
```

### Standard

```vue
<template>
  <div class="q-gutter-y-md column" style="max-width: 300px">
    <q-toggle v-model="dense" label="Dense QField" />

    <q-field :dense="dense">
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field label="Label" stack-label :dense="dense">
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field :dense="dense">
      <template #prepend>
        <q-icon name="event" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field :dense="dense">
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-avatar>
          <img
            alt="Quasar logo"
            src="https://cdn.quasar.dev/logo-v2/svg/logo.svg"
          />
        </q-avatar>
      </template>
    </q-field>

    <q-field
      :model-value="text"
      bottom-slots
      label="Label"
      stack-label
      counter
      :dense="dense"
    >
      <template #prepend>
        <q-icon name="place" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-icon name="close" class="cursor-pointer" />
      </template>

      <template #hint> Field hint </template>
    </q-field>

    <q-field
      :model-value="text"
      bottom-slots
      label="Label"
      stack-label
      counter
      maxlength="12"
      :dense="dense"
    >
      <template #before>
        <q-icon name="flight_takeoff" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-icon v-if="text !== ''" name="close" class="cursor-pointer" />
        <q-icon name="search" />
      </template>

      <template #hint> Field hint </template>
    </q-field>

    <q-field
      :model-value="text"
      bottom-slots
      label="Label"
      stack-label
      counter
      maxlength="12"
      :dense="dense"
    >
      <template #before>
        <q-avatar>
          <img
            alt="User avatar"
            src="https://cdn.quasar.dev/img/avatar5.jpg"
          />
        </q-avatar>
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-icon v-if="text !== ''" name="close" class="cursor-pointer" />
        <q-icon name="schedule" />
      </template>

      <template #hint> Field hint </template>

      <template #after>
        <q-btn round dense flat icon="send" />
      </template>
    </q-field>

    <q-field
      :model-value="text"
      bottom-slots
      label="Label"
      stack-label
      counter
      maxlength="12"
      :dense="dense"
    >
      <template #before>
        <q-icon name="event" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #hint> Field hint </template>

      <template #append>
        <q-btn round dense flat icon="add" />
      </template>
    </q-field>

    <q-field hint="Disable" :dense="dense" disable>
      <template #control>
        <div class="self-center full-width no-outline">{{ text }}</div>
      </template>
    </q-field>

    <q-field hint="Readonly" :dense="dense" readonly>
      <template #control>
        <div class="self-center full-width no-outline">{{ text }}</div>
      </template>
    </q-field>

    <q-field hint="Disable and readonly" :dense="dense" disable readonly>
      <template #control>
        <div class="self-center full-width no-outline">{{ text }}</div>
      </template>
    </q-field>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const text = ref('Field content')
const dense = ref(false)
</script>
```

### Filled

```vue
<template>
  <div class="q-gutter-y-md column" style="max-width: 300px">
    <q-toggle v-model="dense" label="Dense QField" />

    <q-field filled :dense="dense">
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field filled label="Label" stack-label :dense="dense">
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field filled square hint="With perfect square borders" :dense="dense">
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field filled :dense="dense">
      <template #prepend>
        <q-icon name="event" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field filled :dense="dense">
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-avatar>
          <img
            alt="Quasar logo"
            src="https://cdn.quasar.dev/logo-v2/svg/logo.svg"
          />
        </q-avatar>
      </template>
    </q-field>

    <q-field
      filled
      :model-value="text"
      bottom-slots
      label="Label"
      stack-label
      counter
      :dense="dense"
    >
      <template #prepend>
        <q-icon name="place" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-icon name="close" class="cursor-pointer" />
      </template>

      <template #hint> Field hint </template>
    </q-field>

    <q-field
      filled
      :model-value="text"
      bottom-slots
      label="Label"
      stack-label
      counter
      maxlength="12"
      :dense="dense"
    >
      <template #before>
        <q-icon name="flight_takeoff" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-icon v-if="text !== ''" name="close" class="cursor-pointer" />
        <q-icon name="search" />
      </template>

      <template #hint> Field hint </template>
    </q-field>

    <q-field
      filled
      :model-value="text"
      bottom-slots
      label="Label"
      stack-label
      counter
      maxlength="12"
      :dense="dense"
    >
      <template #before>
        <q-avatar>
          <img
            alt="User avatar"
            src="https://cdn.quasar.dev/img/avatar5.jpg"
          />
        </q-avatar>
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-icon v-if="text !== ''" name="close" class="cursor-pointer" />
        <q-icon name="schedule" />
      </template>

      <template #hint> Field hint </template>

      <template #after>
        <q-btn round dense flat icon="send" />
      </template>
    </q-field>

    <q-field
      filled
      :model-value="text"
      bottom-slots
      label="Label"
      stack-label
      counter
      maxlength="12"
      :dense="dense"
    >
      <template #before>
        <q-icon name="event" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #hint> Field hint </template>

      <template #append>
        <q-btn round dense flat icon="add" />
      </template>
    </q-field>

    <q-field filled hint="Disable" :dense="dense" disable>
      <template #control>
        <div class="self-center full-width no-outline">{{ text }}</div>
      </template>
    </q-field>

    <q-field filled hint="Readonly" :dense="dense" readonly>
      <template #control>
        <div class="self-center full-width no-outline">{{ text }}</div>
      </template>
    </q-field>

    <q-field
      filled
      hint="Disable and readonly"
      :dense="dense"
      disable
      readonly
    >
      <template #control>
        <div class="self-center full-width no-outline">{{ text }}</div>
      </template>
    </q-field>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const text = ref('Field content')
const dense = ref(false)
</script>
```

### Outlined

```vue
<template>
  <div class="q-gutter-y-md column" style="max-width: 300px">
    <q-toggle v-model="dense" label="Dense QField" />

    <q-field outlined :dense="dense">
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field outlined label="Label" stack-label :dense="dense">
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field
      outlined
      square
      hint="With perfect square borders"
      :dense="dense"
    >
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field outlined :dense="dense">
      <template #prepend>
        <q-icon name="event" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field outlined :dense="dense">
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-avatar>
          <img
            alt="Quasar logo"
            src="https://cdn.quasar.dev/logo-v2/svg/logo.svg"
          />
        </q-avatar>
      </template>
    </q-field>

    <q-field
      outlined
      :model-value="text"
      bottom-slots
      label="Label"
      stack-label
      counter
      :dense="dense"
    >
      <template #prepend>
        <q-icon name="place" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-icon name="close" class="cursor-pointer" />
      </template>

      <template #hint> Field hint </template>
    </q-field>

    <q-field
      outlined
      :model-value="text"
      bottom-slots
      label="Label"
      stack-label
      counter
      maxlength="12"
      :dense="dense"
    >
      <template #before>
        <q-icon name="flight_takeoff" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-icon v-if="text !== ''" name="close" class="cursor-pointer" />
        <q-icon name="search" />
      </template>

      <template #hint> Field hint </template>
    </q-field>

    <q-field
      outlined
      :model-value="text"
      bottom-slots
      label="Label"
      stack-label
      counter
      maxlength="12"
      :dense="dense"
    >
      <template #before>
        <q-avatar>
          <img
            alt="User avatar"
            src="https://cdn.quasar.dev/img/avatar5.jpg"
          />
        </q-avatar>
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-icon v-if="text !== ''" name="close" class="cursor-pointer" />
        <q-icon name="schedule" />
      </template>

      <template #hint> Field hint </template>

      <template #after>
        <q-btn round dense flat icon="send" />
      </template>
    </q-field>

    <q-field
      outlined
      :model-value="text"
      bottom-slots
      label="Label"
      stack-label
      counter
      maxlength="12"
      :dense="dense"
    >
      <template #before>
        <q-icon name="event" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #hint> Field hint </template>

      <template #append>
        <q-btn round dense flat icon="add" />
      </template>
    </q-field>

    <q-field outlined hint="Disable" :dense="dense" disable>
      <template #control>
        <div class="self-center full-width no-outline">{{ text }}</div>
      </template>
    </q-field>

    <q-field outlined hint="Readonly" :dense="dense" readonly>
      <template #control>
        <div class="self-center full-width no-outline">{{ text }}</div>
      </template>
    </q-field>

    <q-field
      outlined
      hint="Disable and readonly"
      :dense="dense"
      disable
      readonly
    >
      <template #control>
        <div class="self-center full-width no-outline">{{ text }}</div>
      </template>
    </q-field>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const text = ref('Field content')
const dense = ref(false)
</script>
```

### Standout

```vue
<template>
  <div class="q-gutter-y-md column" style="max-width: 300px">
    <q-toggle v-model="dense" label="Dense QInput" />

    <q-field standout :dense="dense">
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field standout label="Label" stack-label :dense="dense">
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field
      standout="bg-teal text-white"
      label="Custom standout"
      stack-label
      :dense="dense"
    >
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field
      standout
      square
      hint="With perfect square borders"
      :dense="dense"
    >
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field standout :dense="dense">
      <template #prepend>
        <q-icon name="event" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field standout :dense="dense">
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-avatar>
          <img
            alt="Quasar logo"
            src="https://cdn.quasar.dev/logo-v2/svg/logo.svg"
          />
        </q-avatar>
      </template>
    </q-field>

    <q-field
      standout
      :model-value="text"
      bottom-slots
      label="Label"
      stack-label
      counter
      :dense="dense"
    >
      <template #prepend>
        <q-icon name="place" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-icon name="close" class="cursor-pointer" />
      </template>

      <template #hint> Field hint </template>
    </q-field>

    <q-field
      standout
      :model-value="text"
      bottom-slots
      label="Label"
      stack-label
      counter
      maxlength="12"
      :dense="dense"
    >
      <template #before>
        <q-icon name="flight_takeoff" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-icon v-if="text !== ''" name="close" class="cursor-pointer" />
        <q-icon name="search" />
      </template>

      <template #hint> Field hint </template>
    </q-field>

    <q-field
      standout
      :model-value="text"
      bottom-slots
      label="Label"
      stack-label
      counter
      maxlength="12"
      :dense="dense"
    >
      <template #before>
        <q-avatar>
          <img
            alt="User avatar"
            src="https://cdn.quasar.dev/img/avatar5.jpg"
          />
        </q-avatar>
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-icon v-if="text !== ''" name="close" class="cursor-pointer" />
        <q-icon name="schedule" />
      </template>

      <template #hint> Field hint </template>

      <template #after>
        <q-btn round dense flat icon="send" />
      </template>
    </q-field>

    <q-field
      standout
      :model-value="text"
      bottom-slots
      label="Label"
      stack-label
      counter
      maxlength="12"
      :dense="dense"
    >
      <template #before>
        <q-icon name="event" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #hint> Field hint </template>

      <template #append>
        <q-btn round dense flat icon="add" />
      </template>
    </q-field>

    <q-field standout hint="Disable" :dense="dense" disable>
      <template #control>
        <div class="self-center full-width no-outline">{{ text }}</div>
      </template>
    </q-field>

    <q-field standout hint="Readonly" :dense="dense" readonly>
      <template #control>
        <div class="self-center full-width no-outline">{{ text }}</div>
      </template>
    </q-field>

    <q-field
      standout
      hint="Disable and readonly"
      :dense="dense"
      disable
      readonly
    >
      <template #control>
        <div class="self-center full-width no-outline">{{ text }}</div>
      </template>
    </q-field>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const text = ref('Field content')
const dense = ref(false)
</script>
```

One of the most appropriate use cases for Standout design is in a QToolbar:

Example "Standout in QToolbar":

```vue
<template>
  <div class="q-gutter-y-md column" style="width: 300px; max-width: 100%">
    <q-toolbar class="bg-primary text-white rounded-borders">
      <q-btn round dense flat icon="menu" class="q-mr-xs" />
      <q-avatar class="gt-xs">
        <img
          alt="Quasar logo"
          src="https://cdn.quasar.dev/logo-v2/svg/logo-mono-white.svg"
        />
      </q-avatar>

      <q-space />
      <q-field dark dense standout>
        <template #control>
          <div class="self-center no-outline" tabindex="0"
            >Time is {{ value }}</div
          >
        </template>
        <template #append>
          <q-btn
            flat
            round
            dense
            :disable="value < 10"
            icon="replay_10"
            @click.stop.prevent="value -= 10"
          />
          <q-btn
            flat
            round
            dense
            :disable="value > 90"
            icon="forward_10"
            @click.stop.prevent="value += 10"
          />
        </template>
      </q-field>
    </q-toolbar>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const value = ref(50)
</script>
```

### Borderless

The `borderless` design allows you to seamlessly integrate your QField into other components without QField drawing a border around itself or changing its background color:

```vue
<template>
  <div class="q-gutter-y-md column" style="width: 300px; max-width: 100%">
    <q-toolbar class="bg-primary text-white rounded-borders">
      <q-btn round dense flat icon="menu" class="q-mr-xs" />
      <q-avatar class="gt-xs">
        <img
          alt="Quasar logo"
          src="https://cdn.quasar.dev/logo-v2/svg/logo-mono-white.svg"
        />
      </q-avatar>

      <q-space />
      <q-field dark borderless>
        <template #control>
          <div class="self-center no-outline" tabindex="0"
            >Time is {{ value }}</div
          >
        </template>
        <template #append>
          <q-btn
            color="white"
            flat
            round
            dense
            :disable="value < 10"
            icon="replay_10"
            @click.stop.prevent="value -= 10"
          />
          <q-btn
            color="white"
            flat
            round
            dense
            :disable="value > 90"
            icon="forward_10"
            @click.stop.prevent="value += 10"
          />
        </template>
      </q-field>
    </q-toolbar>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const value = ref(50)
</script>
```

### Rounded design

The `rounded` prop only works along with Filled, Outlined and Standout designs, as showcased in the example below:

Example "Rounded":

```vue
<template>
  <div class="q-gutter-y-md column" style="max-width: 300px">
    <q-field rounded filled>
      <template #prepend>
        <q-icon name="event" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field rounded outlined>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-avatar>
          <img
            alt="Quasar logo"
            src="https://cdn.quasar.dev/logo-v2/svg/logo.svg"
          />
        </q-avatar>
      </template>
    </q-field>

    <q-field
      rounded
      standout
      bottom-slots
      :model-value="text"
      label="Label"
      stack-label
      counter
    >
      <template #prepend>
        <q-icon name="place" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-icon name="close" class="cursor-pointer" />
      </template>

      <template #hint> Field hint </template>
    </q-field>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const text = ref('Field content')
</script>
```

### Square borders

The `square` prop only makes sense along with Filled, Outlined and Standout designs, as showcased in the example below:

```vue
<template>
  <div class="q-gutter-y-md column" style="max-width: 300px">
    <q-field square filled>
      <template #prepend>
        <q-icon name="event" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>

    <q-field square outlined>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-avatar>
          <img
            alt="Quasar logo"
            src="https://cdn.quasar.dev/logo-v2/svg/logo.svg"
          />
        </q-avatar>
      </template>
    </q-field>

    <q-field
      square
      standout
      bottom-slots
      :model-value="text"
      label="Label"
      stack-label
      counter
    >
      <template #prepend>
        <q-icon name="place" />
      </template>

      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>

      <template #append>
        <q-icon name="close" @click="text = ''" class="cursor-pointer" />
      </template>

      <template #hint> Field hint </template>
    </q-field>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const text = ref('Field content')
</script>
```

### Force dark mode

```vue
<template>
  <div class="bg-grey-9 text-white">
    <div class="q-gutter-y-md column" style="max-width: 300px">
      <div>
        <q-toggle v-model="readonly" label="Readonly" dark />
        <q-toggle v-model="disable" label="Disable" dark />
      </div>

      <q-field dark :readonly="readonly" :disable="disable">
        <template #prepend>
          <q-icon name="event" />
        </template>

        <template #control>
          <div class="self-center full-width no-outline" :tabindex="tabindex">{{
            text
          }}</div>
        </template>
      </q-field>

      <q-field dark filled :readonly="readonly" :disable="disable">
        <template #prepend>
          <q-icon name="event" />
        </template>

        <template #control>
          <div class="self-center full-width no-outline" :tabindex="tabindex">{{
            text
          }}</div>
        </template>
      </q-field>

      <q-field dark outlined :readonly="readonly" :disable="disable">
        <template #control>
          <div class="self-center full-width no-outline" :tabindex="tabindex">{{
            text
          }}</div>
        </template>

        <template #append>
          <q-avatar>
            <img
              alt="Quasar logo"
              src="https://cdn.quasar.dev/logo-v2/svg/logo-dark.svg"
            />
          </q-avatar>
        </template>
      </q-field>

      <q-field
        dark
        standout
        bottom-slots
        :model-value="text"
        label="Label"
        stack-label
        counter
        :readonly="readonly"
        :disable="disable"
      >
        <template #prepend>
          <q-icon name="place" />
        </template>

        <template #control>
          <div class="self-center full-width no-outline" :tabindex="tabindex">{{
            text
          }}</div>
        </template>

        <template #append>
          <q-icon name="close" @click="text = ''" class="cursor-pointer" />
        </template>

        <template #hint> Field hint </template>
      </q-field>

      <q-field dark borderless :readonly="readonly" :disable="disable">
        <template #control>
          <div class="self-center full-width no-outline" :tabindex="tabindex">{{
            text
          }}</div>
        </template>

        <template #append>
          <q-icon name="search" />
        </template>
      </q-field>
    </div>
  </div>
</template>

<script setup>
import { computed, ref } from 'vue'

const text = ref('Field content')
const readonly = ref(false)
const disable = ref(false)
const tabindex = computed(() => (disable.value || readonly.value ? -1 : 0))
</script>
```

## Basic features

### Clearable

As a helper, you can use `clearable` prop so user can reset model to `null` through an appended icon.

> [!IMPORTANT]
> If using `clearable` you must use `v-model` or listen on `@update:model-value` and update the value.

```vue
<template>
  <div class="q-gutter-y-md column" style="max-width: 300px">
    <q-field
      color="orange"
      filled
      v-model="text"
      label="Label"
      stack-label
      clearable
    >
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0"
          >Text is <q>{{ text === null ? 'null' : text }}</q></div
        >
      </template>
      <template v-if="text === null" #append>
        <q-icon
          name="short_text"
          @click.stop.prevent="text = 'Some text'"
          class="cursor-pointer"
        />
      </template>
    </q-field>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const text = ref('Some text')
</script>
```

### Control types

Anything you place inside the `control` slot will be used as content of the field. We provide a few examples of controls below.

```vue
<template>
  <div class="q-gutter-md column" style="max-width: 300px">
    <q-field
      filled
      :hint="`Slider with value ${slider}`"
      :model-value="slider"
      @update:model-value="val => val === null && (slider = 50)"
      clearable
    >
      <template #control>
        <q-slider
          :model-value="slider"
          @change="
            val => {
              slider = val
            }
          "
          :min="0"
          :max="100"
          label
          label-always
          class="q-mt-lg"
        />
      </template>
    </q-field>

    <q-field
      filled
      :hint="`Range between ${range.min} and ${range.max}`"
      :model-value="range"
      @update:model-value="
        val => val === null && (range = { min: 0, max: 100 })
      "
      clearable
    >
      <template #control>
        <q-range
          :model-value="range"
          @change="
            val => {
              range = val
            }
          "
          :min="0"
          :max="100"
        />
      </template>
    </q-field>

    <q-field
      filled
      :hint="`Knob with value ${knob}`"
      :model-value="knob"
      @update:model-value="val => val === null && (knob = 50)"
      clearable
    >
      <template #control>
        <div class="full-width">
          <q-knob
            :model-value="knob"
            @change="
              val => {
                knob = val
              }
            "
            :min="0"
            :max="100"
            size="72px"
            :thickness="1"
            color="light-blue"
            track-color="grey-8"
          />
        </div>
      </template>
    </q-field>

    <q-field
      filled
      :hint="`Calendar with value ${date}`"
      label="Pick a date"
      stack-label
    >
      <template #control>
        <q-date class="q-mt-sm full-width" minimal v-model="date" />
      </template>
    </q-field>

    <q-field
      filled
      :hint="`Time with value ${time}`"
      label="Pick a time"
      stack-label
    >
      <template #control>
        <div class="q-mt-sm full-width">
          <q-time v-model="time" />
        </div>
      </template>
    </q-field>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const slider = ref(50)
const range = ref({
  min: 10,
  max: 30
})
const knob = ref(50)
const time = ref('')
const date = ref('')
</script>
```

> [!TIP]
> Most of the form controls always render something visible, so you if you're using a `label` then you might want to set it along with `stack-label`, otherwise the label will overlap the enclosed control.

### Prefix and suffix

```vue
<template>
  <div class="q-gutter-y-md column" style="max-width: 300px">
    <q-field filled :model-value="email" suffix="@gmail.com">
      <template #before>
        <q-icon name="mail" />
      </template>

      <template #control>
        <div
          class="self-center full-width no-outline text-right"
          tabindex="0"
          >{{ email }}</div
        >
      </template>
    </q-field>

    <q-field outlined :model-value="number" prefix="$">
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          number
        }}</div>
      </template>

      <template #append>
        <q-avatar>
          <img
            alt="Quasar logo"
            src="https://cdn.quasar.dev/logo-v2/svg/logo.svg"
          />
        </q-avatar>
      </template>
    </q-field>

    <q-field
      standout
      :model-value="email"
      prefix="Email:"
      suffix="@gmail.com"
    >
      <template #prepend>
        <q-icon name="mail" />
      </template>

      <template #control>
        <div
          class="self-center full-width no-outline text-right"
          tabindex="0"
          >{{ email }}</div
        >
      </template>
    </q-field>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const email = ref('john.doe')
const number = ref(123)
</script>
```

### Custom Label

Using the `label` slot you can customize the aspect of the label or add special features as `QTooltip`.

> [!IMPORTANT]
> Do not forget to set the `label-slot` property.
>
> If you want to interact with the content of the label (QTooltip) add the `all-pointer-events` class on the element in the slot.

```vue
<template>
  <div class="q-gutter-y-md column" style="max-width: 300px">
    <q-field filled :model-value="email" suffix="@gmail.com" label-slot>
      <template #label>
        <div class="row items-center all-pointer-events">
          <q-icon
            class="q-mr-xs"
            color="deep-orange"
            size="24px"
            name="mail"
          />
          Email (hover for more info)

          <q-tooltip
            class="bg-grey-8"
            anchor="top left"
            self="bottom left"
            :offset="[0, 8]"
            >Email address</q-tooltip
          >
        </div>
      </template>

      <template #control>
        <div
          class="self-center full-width no-outline text-right"
          tabindex="0"
          >{{ email }}</div
        >
      </template>
    </q-field>

    <q-field outlined :model-value="number" prefix="$" label-slot>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          number
        }}</div>
      </template>

      <template #append>
        <q-avatar>
          <img
            alt="Quasar logo"
            src="https://cdn.quasar.dev/logo-v2/svg/logo.svg"
          />
        </q-avatar>
      </template>

      <template #label>
        <span class="text-weight-bold text-deep-orange">You</span>
        can customize the
        <span
          class="q-px-sm bg-deep-orange text-white text-italic rounded-borders"
          >label</span
        >
      </template>
    </q-field>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const email = ref('john.doe')
const number = ref(123)
</script>
```

### Slots with QBtn type "submit"

> [!IMPORTANT]
> When placing a QBtn with type "submit" in one of the "before", "after", "prepend", or "append" slots of a QField, QInput or QSelect, you should also add a `@click` listener on the QBtn in question. This listener should call the method that submits your form. All "click" events in such slots are not propagated to their parent elements.

### Loading state

```vue
<template>
  <div class="q-gutter-y-md column" style="max-width: 300px">
    <q-field :loading="loadingState" filled label="Label" stack-label>
      <template #control>
        <div class="self-center full-width no-outline" tabindex="0">{{
          text
        }}</div>
      </template>
    </q-field>
    <q-toggle v-model="loadingState" label="Loading state" />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const text = ref('Field content')
const loadingState = ref(false)
</script>
```

## Validation

### Internal validation

You can validate QField components with `:rules` prop. Specify array of embedded rules or your own validators. Your custom validator will be a function which returns `true` if validator succeeds or `String` with error message if it doesn't succeed.

> [!NOTE]
> By default, for perf reasons, a change in the rules does not trigger a new validation until the model changes. In order to trigger the validation when rules change too, then use `reactive-rules` Boolean prop. The downside is a performance penalty (so use it when you really need this only!) and it can be slightly mitigated by using a computed prop as value for the rules (and not specify them inline in the vue template).

This is so you can write convenient rules of shape like:

```js
value => condition || errorMessage

// example:
value => value < 10 || 'Value should be lower'
```

You can reset the validation by calling `resetValidation()` method on the QField.

Example "Basic":

```vue
<template>
  <div style="max-width: 400px">
    <q-field
      ref="fieldRef"
      filled
      v-model="date"
      label="Required Field"
      stack-label
      :rules="[val => !!val || 'Field is required']"
    >
      <template #control>
        <q-date
          class="q-mt-sm full-width"
          style="width: 300px"
          minimal
          v-model="date"
        />
      </template>
    </q-field>

    <div class="q-mt-sm">
      <div class="q-gutter-sm">
        <q-btn
          label="Reset Validation"
          @click="resetValidation"
          color="primary"
        />
        <q-btn label="Reset Date" @click="resetDate" color="primary" />
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, useTemplateRef } from 'vue'

const date = ref('')
const fieldRef = useTemplateRef('fieldRef')

function resetValidation() {
  fieldRef.value.resetValidation()
}

function resetDate() {
  date.value = ''
}
</script>
```

Example "Maximum value":

```vue
<template>
  <div style="max-width: 300px">
    <q-field
      ref="fieldRef"
      filled
      :model-value="slider"
      label="Maximum 60"
      stack-label
      :rules="[val => val <= 60 || 'Please set value to maximum 60']"
    >
      <template #control>
        <q-slider
          v-model="slider"
          :min="0"
          :max="100"
          label
          label-always
          class="q-mt-lg"
          style="width: 200px"
        />
      </template>
    </q-field>

    <q-btn
      class="q-mt-sm"
      label="Reset Validation"
      @click="reset"
      color="primary"
    />
  </div>
</template>

<script setup>
import { ref, useTemplateRef } from 'vue'

const fieldRef = useTemplateRef('fieldRef')
const slider = ref(50)

function reset() {
  fieldRef.value.resetValidation()
}
</script>
```

If you set `lazy-rules`, validation triggers when the field loses focus (a `readonly` field included, only a `disable`d field is exempt from validation); while an error is displayed, the field re-validates on each change so the error clears as soon as the value becomes valid. A menu or dialog opened from inside the field (a QPopupProxy in the `append` slot, for instance) keeps the field focused for as long as it is open, so it does not count as losing focus. If `lazy-rules` is set to `ondemand` String, then validation will be triggered only when component's validate() method is manually called or when the wrapper QForm submits itself.

Example "Lazy rules":

```vue
<template>
  <div style="max-width: 300px">
    <q-field
      ref="fieldRef"
      filled
      :model-value="slider"
      label="Value must be less than 60"
      hint="Validation starts after first blur"
      :rules="[val => val < 60 || 'Please set value to maximum 60']"
      lazy-rules
    >
      <template #control>
        <q-slider
          v-model="slider"
          :min="0"
          :max="100"
          label
          label-always
          class="q-mt-lg"
          style="width: 200px"
        />
      </template>
    </q-field>

    <q-btn
      class="q-mt-sm"
      label="Reset Validation"
      @click="reset"
      color="primary"
    />
  </div>
</template>

<script setup>
import { ref, useTemplateRef } from 'vue'

const fieldRef = useTemplateRef('fieldRef')
const slider = ref(50)

function reset() {
  fieldRef.value.resetValidation()
}
</script>
```

#### Async rules

Rules can be async too, by using async/await or by directly returning a Promise. If the value changes or the field gets blurred while an async validation is still in flight, the field re-validates once it settles, so the displayed verdict always matches the current value.

> [!TIP]
> Consider coupling async rules with `debounce` prop to avoid calling the async rules immediately on each keystroke, which might be detrimental to performance.

```vue
<template>
  <div style="max-width: 350px">
    <q-field
      ref="fieldRef"
      filled
      :model-value="slider"
      hint="Pick between 10 and 60"
      :rules="[myRule]"
    >
      <template #control>
        <q-slider
          v-model="slider"
          :min="0"
          :max="100"
          label
          label-always
          class="q-mt-lg"
          style="width: 200px"
        />
      </template>
    </q-field>

    <q-btn
      class="q-mt-sm"
      label="Reset Validation"
      @click="reset"
      color="primary"
    />
  </div>
</template>

<script setup>
import { ref, useTemplateRef } from 'vue'

const fieldRef = useTemplateRef('fieldRef')
const slider = ref(10)

function myRule(val) {
  // simulating a delay
  return new Promise(resolve => {
    setTimeout(() => {
      // call
      //  resolve(true)
      //     --> content is valid
      //  resolve(false)
      //     --> content is NOT valid, no error message
      //  resolve(error_message)
      //     --> content is NOT valid, we have error message
      resolve((val >= 10 && val <= 60) || 'Please set value to maximum 60')

      // calling reject(...) will also mark the input
      // as having an error, but there will not be any
      // error message displayed below the input
      // (only in browser console)
    }, 1000)
  })
}

function reset() {
  fieldRef.value.resetValidation()
}
</script>
```

### External validation

You can also use external validation and only pass `error` and `error-message` (enable `bottom-slots` to display this error message).

> [!TIP]
> Depending on your needs, you might connect [Regle](https://reglejs.dev/) (our recommended approach) or some other validation library to QField.

Example "External":

```vue
<template>
  <div style="max-width: 300px">
    <q-field
      filled
      :model-value="slider"
      label="Move it above 30"
      bottom-slots
      hint="Max value is 30"
      error-message="Please use a maximum value of 30"
      :error="!isValid"
    >
      <template #control>
        <q-slider
          v-model="slider"
          :min="0"
          :max="100"
          label
          label-always
          class="q-mt-lg"
          style="width: 200px"
        />
      </template>
    </q-field>
  </div>
</template>

<script setup>
import { computed, ref } from 'vue'

const slider = ref(10)
const isValid = computed(() => slider.value <= 30)
</script>
```

You can also customize the slot for error message:

Example "Slot for error message":

```vue
<template>
  <div style="max-width: 300px">
    <q-field
      filled
      :model-value="slider"
      label="Move it above 30"
      bottom-slots
      hint="Max value is 30"
      :error="!isValid"
    >
      <template #control>
        <q-slider
          v-model="slider"
          :min="0"
          :max="100"
          label
          label-always
          class="q-mt-lg"
          style="width: 200px"
        />
      </template>
      <template #error> Please use a maximum value of 30. </template>
    </q-field>
  </div>
</template>

<script setup>
import { computed, ref } from 'vue'

const slider = ref(10)
const isValid = computed(() => slider.value <= 30)
</script>
```

## Accessibility *(v2.25+)*

QField renders as a native `<label>` wired through its `for` attribute to the enclosed control, using a generated SSR-safe id (overridable through the `for` prop), so clicking the label focuses the control and screen readers announce the field's name for it. A disabled field carries `aria-disabled` on the wrapper, and the clear icon shown by `clearable` is a keyboard-operable button — activated with <kbd>Enter</kbd> or <kbd>Space</kbd> — with a localized accessible name from the [Quasar Language Pack](../options/quasar-language-packs.md).

A `readonly` field stays in the Tab order (its value can still be read and copied), so it shows the focused state like any other field (the highlight, the floated label, a `hide-hint` hint) and emits `@focus`/`@blur`; only a `disable`d field never counts as focused.

Validation errors are announced as they appear: the error message renders with `role="alert"`, and while it is displayed the control also receives `aria-invalid` together with `aria-errormessage` and `aria-describedby` pointing at the message. These references are applied only while the message actually renders, so they never point at a missing element, and an `aria-describedby` you set yourself is merged with the error reference rather than replaced by it.

When building your own control through the `control` slot, this wiring is handed to you rather than applied for you: the slot scope exposes `id`, `ariaInvalid`, `ariaDescribedby` and `ariaErrormessage`, and it is your responsibility to bind them to your focusable element (the [third party mask processor examples](input.md#using-third-party-mask-processors) on the QInput page show `:id="id"` in action). The same `id` binding is what receives focus when the field is focused as a whole, whether through its `autofocus` prop, its `focus()` method, a QForm/QDialog/QMenu that autofocuses it, or a click on the wrapper: focus is forwarded to the element carrying the id, or to the first focusable element in the slot when none does. Also note that the `hint` text is purely visual — it is not associated with the control through `aria-describedby` — so if a hint carries essential information, convey it to assistive technology yourself (for instance through your own element referenced by `aria-describedby`).
