---
name: date-range-picker
title: Date Range Picker
url: https://ui.frontic.com/components/ui/date-range-picker.md
tier: ui
install: npx @frontic/ui add date-range-picker
files:
  - components/ui/date-range-picker/DateRangePicker.vue
  - components/ui/date-range-picker/DateRangePickerCalendar.vue
  - components/ui/date-range-picker/index.ts
registryDependencies:
  - date-field
  - date-picker
  - range-calendar
  - use-field-control
dependencies:
  - "@internationalized/date"
  - "@vueuse/core"
  - reka-ui
---

# Date Range Picker

A start and an end date typed segment by segment or picked from a calendar the button beside them opens; joins a Field around it.

## Install

```sh
npx @frontic/ui add date-range-picker
```

Built from: [Date Field](https://ui.frontic.com/components/ui/date-field.md), [Date Picker](https://ui.frontic.com/components/ui/date-picker.md), [Range Calendar](https://ui.frontic.com/components/ui/range-calendar.md), [Use Field Control](https://ui.frontic.com/composables/use-field-control.md)

npm packages: `@internationalized/date`, `@vueuse/core`, `reka-ui`

## Examples

```vue
<script setup lang="ts">
import { getLocalTimeZone, today } from '@internationalized/date'
import { Field, FieldDescription, FieldLabel } from '@/components/ui/field'
import { DateRangePicker } from '@/components/ui/date-range-picker'

const tomorrow = today(getLocalTimeZone()).add({ days: 1 })
</script>

<template>
  <Field name="away" class="w-sm max-w-full">
    <FieldLabel :native-label="false">Pause deliveries while you're away</FieldLabel>
    <DateRangePicker :min-value="tomorrow" :maximum-days="28" />
    <FieldDescription>We hold your parcels at the Basecamp store, for up to four weeks.</FieldDescription>
  </Field>
</template>
```

The example also uses `field`, which `add date-range-picker` leaves out: `npx @frontic/ui add field`.

## API reference

### DateRangePicker

Lets the user type a start and an end date, or pick both from a calendar the button beside them opens.

`data-slot="date-range-picker"` · `DateRangePicker.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `disabled` | `boolean` | `false` | Whether or not the date field is disabled |
| `dir` | `Direction`: `'ltr' \| 'rtl'` |  | The reading direction of the date field when applicable.<br><br>If omitted, inherits globally from `ConfigProvider` or assumes LTR (left-to-right) reading mode. |
| `modelValue` | `DateRange \| null`: `null \| DateRange` |  | The controlled value of the field. Can be bound as `v-model`. |
| `defaultValue` | `DateRange` |  | The default value for the calendar |
| `name` | `string` |  | The name of the field. Submitted with its owning form as part of a name/value pair. |
| `required` | `boolean` | `false` | When `true`, indicates that the user must set the value before the owning form can be submitted. |
| `defaultPlaceholder` | `DateValue`: `CalendarDate \| CalendarDateTime \| ZonedDateTime` |  | The default placeholder date |
| `placeholder` | `DateValue`: `CalendarDate \| CalendarDateTime \| ZonedDateTime` |  | The placeholder date, which is used to determine what month to display when no date is selected. This updates as the user navigates the calendar and can be used to programmatically control the calendar view |
| `maxValue` | `DateValue`: `CalendarDate \| CalendarDateTime \| ZonedDateTime` |  | The maximum date that can be selected |
| `minValue` | `DateValue`: `CalendarDate \| CalendarDateTime \| ZonedDateTime` |  | The minimum date that can be selected |
| `locale` | `string` |  | The locale to use for formatting dates |
| `readonly` | `boolean` | `false` | Whether or not the date field is readonly |
| `isDateUnavailable` | `Matcher`: `(date: DateValue): boolean` |  | A function that returns whether or not a date is unavailable |
| `id` | `string` |  | Id of the element |
| `hourCycle` | `HourCycle`: `12 \| 24` |  | The hour cycle used for formatting times. Defaults to the local preference |
| `step` | `DateStep` |  | The stepping interval for the time fields. Defaults to `1`. |
| `granularity` | `Granularity`: `'day' \| 'hour' \| 'minute' \| 'second'` |  | The granularity to use for formatting times. Defaults to day if a CalendarDate is provided, otherwise defaults to minute. The field will render segments for each part of the date up to and including the specified granularity |
| `hideTimeZone` | `boolean` | `false` | Whether or not to hide the time zone segment of the field |
| `defaultOpen` | `boolean` | `false` | The open state of the popover when it is initially rendered. Use when you do not need to control its open state. |
| `open` | `boolean` | `false` | The controlled open state of the popover. |
| `modal` | `boolean` | `false` | The modality of the popover. When set to true, interaction with outside elements will be disabled and only popover content will be visible to screen readers. |
| `pagedNavigation` | `boolean` | `false` | This property causes the previous and next buttons to navigate by the number of months displayed at once, rather than one month |
| `preventDeselect` | `boolean` | `false` | Whether or not to prevent the user from deselecting a date without selecting another date first |
| `weekStartsOn` | `WeekStartsOn`: `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` |  | The day of the week to start the calendar on |
| `weekdayFormat` | `WeekDayFormat`: `'narrow' \| 'short' \| 'long'` |  | The format to use for the weekday strings provided via the weekdays slot prop |
| `fixedWeeks` | `boolean` | `false` | Whether or not to always display 6 weeks in the calendar |
| `numberOfMonths` | `number` |  | The number of months to display at once |
| `isDateDisabled` | `Matcher`: `(date: DateValue): boolean` |  | A function that returns whether or not a date is disabled |
| `isDateHighlightable` | `Matcher`: `(date: DateValue): boolean` |  | A function that returns whether or not a date is hightable |
| `allowNonContiguousRanges` | `boolean` | `false` | When combined with `isDateUnavailable`, determines whether non-contiguous ranges, i.e. ranges containing unavailable dates, may be selected. |
| `fixedDate` | `'start' \| 'end'` |  | Which part of the range should be fixed |
| `maximumDays` | `number` |  | The maximum number of days that can be selected in a range |
| `closeOnSelect` | `boolean` | `true` | Whether or not to close the popover on range select |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |
| `triggerLabel` | `string` | `'Open calendar'` | Names the calendar button for screen readers. |
| `separator` | `string` | `'–'` | Sits between the start and the end. |

| Event | Payload | Description |
| --- | --- | --- |
| `update:modelValue` | `date: DateRange` | Event handler called whenever the model value changes |
| `update:open` | `value: boolean` | Event handler called when the open state of the popover changes. |
| `update:placeholder` | `date: DateValue` | Event handler called whenever the placeholder value changes |
| `update:startValue` | `date: DateValue \| undefined` | Event handler called whenever the start value changes |

| Slot | Slot props | Description |
| --- | --- | --- |
| `default` | `{ segments: { start: Segment[]; end: Segment[]; }; modelValue: DateRange; }` | Replaces the segments; gets the start's and the end's and the range, to lay out with DateFieldInput. |

## Styling

| Part | Recipe |
| --- | --- |
| `separator` | [`date-range-picker-separator`](https://ui.frontic.com/DESIGN.md#date-range-picker-separator) |

States with a look of their own: invalid.

## Source

`components/ui/date-range-picker/DateRangePicker.vue`

```vue
<script lang="ts">
/** Lets the user type a start and an end date, or pick both from a calendar the button beside them opens. */
export default {}
</script>

<script setup lang="ts">
import type { DateRange, DateRangePickerRootEmits, DateRangePickerRootProps, SegmentPart } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { DateRangePickerAnchor, DateRangePickerRoot, useForwardPropsEmits } from 'reka-ui'
import { computed, nextTick, useTemplateRef } from 'vue'
import { DateField, DateFieldInput } from '@library/components/ui/date-field'
import { DatePickerContent, DatePickerTrigger } from '@library/components/ui/date-picker'
import DateRangePickerCalendar from './DateRangePickerCalendar.vue'
import { useFieldControl } from '@library/composables/use-field-control'

type Segment = { part: SegmentPart; value: string }

const props = withDefaults(
  defineProps<
    DateRangePickerRootProps & {
      class?: HTMLAttributes['class']
      /** Names the calendar button for screen readers. */
      triggerLabel?: string
      /** Sits between the start and the end. */
      separator?: string
    }
  >(),
  { triggerLabel: 'Open calendar', separator: '–', closeOnSelect: true },
)
defineOptions({ inheritAttrs: false })

const emits = defineEmits<DateRangePickerRootEmits>()

const forwarded = useForwardPropsEmits(reactiveOmit(props, 'class', 'triggerLabel', 'separator'), emits)

defineSlots<{
  /** Replaces the segments; gets the start's and the end's and the range, to lay out with DateFieldInput. */
  default?: (props: { segments: { start: Segment[]; end: Segment[] }; modelValue: DateRange }) => unknown
}>()

// Delete once Reka's DateRangePicker reads the Field itself; the group takes the aria and focus.
const group = useTemplateRef<{ $el: HTMLElement }>('group')
const { joined, report, field } = useFieldControl({
  props,
  forwarded,
  root: group,
  element: (root) => root?.querySelector<HTMLElement>('[data-reka-date-field-segment]:not([data-reka-date-field-segment="literal"])'),
  id: () => props.id,
  focus: 'none',
  aria: 'none',
  state: false,
  isFilled: (range) => !!(range as DateRange | undefined)?.start && !!(range as DateRange | undefined)?.end,
})
const fieldAttrs = computed(() =>
  field
    ? {
        ...field.dataAttributes.value,
        'aria-labelledby': field.labelId.value,
        'aria-describedby': field.describedBy.value,
        'aria-invalid': (!joined.value.disabled && field.invalid.value) || undefined,
        onFocusin: () => field.handleControlFocus(),
        onFocusout: (event: FocusEvent) => {
          const next = event.relatedTarget as HTMLElement | null
          // The calendar is teleported out of the group; moving into it is still picking.
          if (group.value?.$el.contains(next) || next?.closest('[data-slot="date-picker-content"]')) return
          field.handleControlBlur()
        },
      }
    : {},
)

// Focus leaving from the teleported calendar never reaches the group's focusout. Once the calendar closes, Reka sends focus back to the button unless the user moved it elsewhere.
async function closed() {
  if (!field) return
  await nextTick()
  if (!group.value?.$el.contains(document.activeElement)) field.handleControlBlur()
}
</script>

<template>
  <DateRangePickerRoot v-bind="joined" @update:model-value="report">
    <DateRangePickerAnchor as-child>
      <DateField ref="group" v-bind="{ ...$attrs, ...fieldAttrs }" data-slot="date-range-picker" :class="props.class">
        <template #default="slotProps">
          <slot v-bind="slotProps as { segments: { start: Segment[]; end: Segment[] }; modelValue: DateRange }">
            <template v-for="type in ['start', 'end'] as const" :key="type">
              <span v-if="type === 'end'" data-slot="date-range-picker-separator" aria-hidden="true" class="fui-date-range-picker-separator">{{ separator }}</span>
              <DateFieldInput v-for="(segment, index) in (slotProps.segments as { start: Segment[]; end: Segment[] })[type]" :key="index" :type="type" :part="segment.part">{{
                segment.value
              }}</DateFieldInput>
            </template>
          </slot>
        </template>
        <template #end>
          <DatePickerTrigger :label="triggerLabel" />
        </template>
      </DateField>
    </DateRangePickerAnchor>
    <DatePickerContent @close-auto-focus="closed">
      <DateRangePickerCalendar />
    </DatePickerContent>
  </DateRangePickerRoot>
</template>
```

`components/ui/date-range-picker/DateRangePickerCalendar.vue`

```vue
<script lang="ts">
/** The calendar a date range picker opens: our RangeCalendar, driven by the picker's dates and settings. */
export default {}
</script>

<script setup lang="ts">
import type { DateRange, DateValue } from 'reka-ui'
import { isEqualDay } from '@internationalized/date'
import { injectDateRangePickerRootContext } from 'reka-ui'
import { RangeCalendar } from '@library/components/ui/range-calendar'

const picker = injectDateRangePickerRootContext()

const same = (a: DateValue | undefined, b: DateValue | undefined) => !!a && !!b && isEqualDay(a, b)

function pick(range: DateRange) {
  const current = picker.modelValue.value
  if (same(range.start, current.start) && same(range.end, current.end)) return
  picker.onDateChange(range)
}

function page(date: DateValue) {
  if (!isEqualDay(date, picker.placeholder.value)) picker.onPlaceholderChange(date)
}
</script>

<template>
  <RangeCalendar
    :model-value="picker.modelValue.value"
    :placeholder="picker.placeholder.value"
    :allow-non-contiguous-ranges="picker.allowNonContiguousRanges.value"
    :is-date-disabled="picker.isDateDisabled"
    :is-date-unavailable="picker.isDateUnavailable"
    :is-date-highlightable="picker.isDateHighlightable"
    :min-value="picker.minValue.value"
    :max-value="picker.maxValue.value"
    :locale="picker.locale.value"
    :disabled="picker.disabled.value"
    :paged-navigation="picker.pagedNavigation.value"
    :week-starts-on="picker.weekStartsOn.value"
    :weekday-format="picker.weekdayFormat.value"
    :fixed-weeks="picker.fixedWeeks.value"
    :number-of-months="picker.numberOfMonths.value"
    :readonly="picker.readonly.value"
    :prevent-deselect="picker.preventDeselect.value"
    :fixed-date="picker.fixedDate.value"
    :maximum-days="picker.maximumDays?.value"
    :dir="picker.dir.value"
    @update:model-value="pick"
    @update:start-value="picker.onStartValueChange"
    @update:placeholder="page"
  />
</template>
```

## API of the items the example uses

### Field

[Full docs](https://ui.frontic.com/components/ui/field.md) · `npx @frontic/ui add field`

#### Field

Groups a label, an input, a description and an error of one form field, and validates its input.

`data-slot="field"` · `Field.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | `string` |  | The name of the field. Submitted with its owning form as part of a name/value pair, and used to match server `errors` on a `FormRoot`. Takes precedence over the `name` of the control. |
| `disabled` | `boolean` | `false` | When `true`, prevents the user from interacting with the field's control. Takes precedence over the `disabled` of the control. |
| `required` | `boolean` | `false` | When `true`, indicates that the user must set the value before the owning form can be submitted. |
| `invalid` | `boolean` |  | When `true`, marks the field invalid regardless of its own validation. Useful when the field state is controlled by an external library. |
| `dirty` | `boolean` |  | Whether the field's value has changed from its initial value. Useful when the field state is controlled by an external library. |
| `touched` | `boolean` |  | Whether the field has been touched (its control blurred). Useful when the field state is controlled by an external library. |
| `validate` | `FieldValidateFn`: `(value: unknown, formValues: Record<string, unknown>): FieldValidateResult \| Promise<FieldValidateResult>` |  | Custom validation function. Return an error message (or array of messages) when invalid, or `null`/`undefined` when valid. Receives the control's value and the values of every named field in the owning form.<br><br>Can be async, but an async `validate` does not prevent form submission when `validationMode` is `onSubmit`. |
| `validationMode` | `FieldValidationMode`: `'onBlur' \| 'onChange' \| 'onSubmit'` | `'onSubmit'` | When the field (re-)runs validation. Takes precedence over the `validationMode` of an ancestor `FormRoot`.<br>- `onSubmit`: when the form is submitted, then on every change after that.<br>- `onBlur`: when the control loses focus.<br>- `onChange`: on every change to the control's value. |
| `validationDebounceTime` | `number` |  | How long to wait (in ms) between `validate` calls when validating on change. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |
| `orientation` | `FieldOrientation`: `'vertical' \| 'horizontal' \| 'responsive'` | `'vertical'` | Label above the control, beside it, or `responsive`: beside it once the field group is wide enough. |

| Slot | Slot props | Description |
| --- | --- | --- |
| `default` | `{ invalid: boolean; errors: string[]; }` | The label, control, description and error; gets `invalid` and the current `errors`. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-horizontal` |  | Present when the part is laid out horizontally. |
| `data-orientation` | `'vertical' \| 'horizontal' \| 'responsive'` | Carries the `orientation` prop. |

#### FieldContent

Groups the label and description that sit beside the control of a field. Renders a `<div>`.

`data-slot="field-content"` · `FieldContent.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'div'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

#### FieldDescription

Gives help text for a field, such as what to enter.

`data-slot="field-description"` · `FieldDescription.vue`

Styling: `field-description`, which extends [`hint`](https://ui.frontic.com/DESIGN.md#hint) and [`link-primary`](https://ui.frontic.com/DESIGN.md#link-primary). Change them there and every part that extends them follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` |  | Id of the element. Auto-generated when not provided. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

#### FieldError

Shows why the input of a field is invalid, from its own validation, from the server or from a form library.

`data-slot="field-error"` · `FieldError.vue`

Styling: `field-error`, which extends [`error`](https://ui.frontic.com/DESIGN.md#error). Change it there and every part that extends it follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` |  | Id of the element. Auto-generated when not provided. |
| `match` | `boolean \| keyof ValidityState`: `boolean \| 'badInput' \| 'customError' \| 'patternMismatch' \| 'rangeOverflow' \| 'rangeUnderflow' \| 'stepMismatch' \| 'tooLong' \| 'tooShort' \| 'typeMismatch' \| 'valid' \| 'valueMissing'` |  | Restricts when this error renders:<br>- a `ValidityState` key (e.g. `"valueMissing"`) — renders when that constraint fails.<br>- `true` — always renders, letting an external library control visibility.<br>- omitted — renders whenever the field is invalid. |
| `forceMount` | `boolean` | `false` | Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |
| `errors` | `(string \| { message: string \| undefined; } \| undefined)[]` |  | Messages from outside the Field, such as vee-validate's or TanStack Form's; shown instead of the Field's own, duplicates dropped. |

| Slot | Slot props | Description |
| --- | --- | --- |
| `default` | `{ errors: string[]; }` | Replaces the messages; gets the current `errors`. |

#### FieldGroup

Stacks several fields of a form together. Renders a `<div>`.

`data-slot="field-group"` · `FieldGroup.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'div'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

#### FieldLabel

Labels the input of a field.

`data-slot="field-label"` · `FieldLabel.vue`

Styling: `field-label`, which extends [`label`](https://ui.frontic.com/DESIGN.md#label). Change it there and every part that extends it follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `nativeLabel` | `boolean` | `true` | Whether the label renders a native `label` element. Set to `false` when rendering another element (e.g. `as="div"`): it then drops `for`, focuses the control on click, and relies on the control's `aria-labelledby`.<br><br>Useful for button controls like `SelectTrigger`, where a native label would forward clicks and `:hover` to the button. |
| `for` | `string` |  | The id of the element the label is associated with. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

#### FieldLegend

Titles a set of related fields. Renders a `<legend>`.

`data-slot="field-legend"` · `FieldLegend.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'legend'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |
| `variant` | `'label' \| 'legend'` | `'legend'` | Whether the legend reads as a section heading or as a field label. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-variant` | `'label' \| 'legend'` | Carries the `variant` prop. |

#### FieldSeparator

Separates fields of a form, optionally with a short text in between. Renders a `<div>`.

`data-slot="field-separator"` · `FieldSeparator.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-content` | `'true' \| 'false'` | `true` when the separator has text in it. |

#### FieldSet

Groups related fields under one legend. Renders a `<fieldset>`.

`data-slot="field-set"` · `FieldSet.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'fieldset'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

#### FieldTitle

Shows the title of a field next to its description. Renders a `<div>`.

`data-slot="field-title"` · `FieldTitle.vue`

Styling: `field-label`, which extends [`label`](https://ui.frontic.com/DESIGN.md#label). Change it there and every part that extends it follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'div'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

#### FieldValidity

Renders from the validation state of a field, such as a custom message or a strength meter.

| Slot | Slot props | Description |
| --- | --- | --- |
| `default` | `{ validity: Omit<Record<keyof ValidityState, boolean>, "valid"> & { valid: boolean \| null; }; errors: string[]; error: string; value: unknown; initialValue: unknown; }` | Gets the field's `validity` (`valid` is `null` until there is one), its `errors`, the first `error`, the `value` validated last and the `initialValue`. |
