Skip to content
v2.5.0
English
Русский
Documentation

DateRangeField

DateRangeField is a compound component for connected start and end date entry that combines typed range input, optional calendar selection, and shared range validation.
date-range-field.tsx · 67 lines
import {
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,
  CalendarViewButtonGroup,
Browser support
Newly available 2024
  • Chrome 125
  • Edge 125
  • Firefox 128
  • Safari 18
  • Safari on iOS 18
Bundle size
Tested in
21 tests

Summary

DateRangeField is a compound component for connected start and end date entry that combines typed range input, optional calendar selection, and shared range validation. It is the field-first range layer when one business concept owns both dates.

Imports

import {
  DateRangeField,
  DateRangeFieldInput,
  DateRangeFieldCalendar,
  DateRangeFieldTriggerIconButton,
  type DateRangeFieldProps,
} from '@vega-ui/react';

Exported Types

  • DateRangeFieldProps
  • DateRangeFieldInputProps
  • DateRangeFieldCalendarProps
  • DateRangeFieldTriggerIconButtonProps

Minimal Composition

minimal-composition.tsx · 67 lines
import {
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,
  CalendarViewButtonGroup,

Required Parts

  • DateRangeField: root state and range-format owner
  • DateRangeFieldInput: typed range entry surface

Optional Parts

  • DateRangeFieldTriggerIconButton: calendar trigger affordance
  • DateRangeFieldCalendar: visual range picker content, usually mounted in Popover

Composition Order

  1. DateRangeField
  2. DateRangeFieldInput
  3. Popover
  4. DateRangeFieldTriggerIconButton
  5. DateRangeFieldCalendar

Variants

  • Input-only versus input-plus-calendar composition
  • Compact filter range versus fuller booking range flows
  • Constraint-driven range state through min, max, and disabledDates

Edge Cases

  • Partial ranges are expected during editing and need explicit handling.
  • Range semantics such as inclusive booking days or reporting cutoffs belong to the consumer.
  • Overlay behavior should be tested inside filters, drawers, and other stacked layouts.
  • Typed range parsing, visual blocked days, and final business validation should stay aligned instead of becoming separate rule systems.

Common Mistakes

  • Passing Date objects or display-format strings to min / max instead of ISO YYYY-MM-DD strings.
  • Treating the start and end date as unrelated values.
  • Leaving the meaning of the range ambiguous in labels or helper text.
  • Relying on visual calendar blocking without matching validation rules.

Examples

Basic

Basic: booking range with a calendar popover

One field for a start and an end date: type both, or pick two days in the calendar. defaultValue is a [start, end] pair of ISO days. Without rangeSeparator the dates are joined by an en dash between non-breaking spaces.

en-US
ru-RU
ar-EG
he-IL
booking-range-with-a-calendar-popover.tsx · 75 lines
import {
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,
  CalendarViewButtonGroup,

Controlled/Stateful

Controlled/Stateful: number of nights

value and onChangeValue on the root keep the [start, end] pair in parent state, so the night count and the price come straight from ISO days. Each side is '' until it is a complete date.

number-of-nights.tsx · 93 lines
import { useState } from 'react';
import {
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,

Controlled/Stateful: preset ranges

Buttons set the controlled range from outside, and the input shows it in format. The presets are computed from today's date in the click handler, so the page renders the same on the server.

preset-ranges.tsx · 106 lines
import { useState } from 'react';
import {
  Button,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,

Form/Integration

Form/Integration: report filter with two API parameters

onChangeValue mirrors the range into two hidden inputs, so FormData holds from and to as ISO days instead of the typed text. required and a pattern for the full range block an empty or one-sided period, and the reset button restores defaultValue.

report-filter-with-two-api-parameters.tsx · 108 lines
import { FormEvent, useState } from 'react';
import {
  Button,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,

Layout/Overlay

Layout/Overlay: range in a filter drawer

The calendar popover opens above the drawer that contains the field. The drawer form uses method='dialog': submitting applies the filter and closes the drawer.

range-in-a-filter-drawer.tsx · 126 lines
import { CSSProperties, useState } from 'react';
import { XIcon } from '@vega-ui/icons';
import {
  Button,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,

Error

Error: both dates required

A single date is a valid entry for the field. Native required and a pattern for the full range make submit fire invalid until both dates are complete; the handler picks the message from validity, and typing clears it.

both-dates-required.tsx · 104 lines
import { FormEvent, useState } from 'react';
import {
  Button,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,

Disabled

Disabled: locked billing period

disabled on the root disables the input and the calendar trigger, and the helper text explains why.

locked-billing-period.tsx · 73 lines
import {
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,
  CalendarViewButtonGroup,

Edge

Edge: reversed and out-of-range dates

A range typed backwards is swapped, so 15.01.2026 - 10.01.2026 becomes 10.01.2026 - 15.01.2026. Each date is clamped to min and max and to the length of its month; the readout shows the resulting pair.

reversed-and-out-of-range-dates.tsx · 90 lines
import { useState } from 'react';
import {
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,

Edge: blackout dates

disabledDates blocks days in the calendar, but a typed range or a picked range around the blocked days still reaches the value. The submit handler checks whether the range covers a blackout day and shows an error.

blackout-dates.tsx · 118 lines
import { FormEvent, useState } from 'react';
import {
  Button,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,

Anatomy

Overview

DateRangeField is a compound component that coordinates a connected start and end date through one field, optional calendar picker, and shared range rules.

Required Parts

DateRangeField

Required. Owns range parsing, formatting, and date constraint rules.

DateRangeFieldInput

Required. Provides the typed start-to-end range entry surface.

Optional Parts

DateRangeFieldTriggerIconButton

Optional trigger affordance for opening a calendar overlay.

DateRangeFieldCalendar

Optional visual range picker that should usually be mounted inside Popover.

Its children are the Calendar parts: CalendarHeader with CalendarPrevButton, CalendarViewButtonGroup (CalendarViewButton view='month' and view='year'), and CalendarNextButton, then CalendarContent with CalendarDays, CalendarMonths, and CalendarYears. The calendar takes locale and calendar from DateRangeField.

Composition Order

  1. DateRangeField
  2. DateRangeFieldInput
  3. Popover
  4. DateRangeFieldTriggerIconButton
  5. DateRangeFieldCalendar
  6. CalendarHeader and CalendarContent with the calendar views

Valid Composition Patterns

minimal-composition.tsx · 67 lines
import {
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,
  CalendarViewButtonGroup,

Invalid Composition Patterns

Range input outside DateRangeField

This breaks coordinated parsing and range selection state.

Range picker without clear range semantics in surrounding copy

Users should not have to guess whether the range is inclusive, report-based, or booking-based.

Treating incomplete ranges as fully valid values

Start and end date flows need explicit handling while the field is partially filled.