DateRangePicker

A date range picker that combines a trigger field with a range calendar popup for selecting start and end dates.

Import

import { DateRangePicker } from 'heroui-native-pro';

Anatomy

<DateRangePicker>
  <Label>...</Label>
  <DateRangePicker.Select>
    <DateRangePicker.Trigger>
      <DateRangePicker.Value />
      <DateRangePicker.TriggerIndicator />
    </DateRangePicker.Trigger>
    <DateRangePicker.Portal>
      <DateRangePicker.Overlay />
      <DateRangePicker.Content>
        <DateRangePicker.Calendar>
          <RangeCalendar.Header>...</RangeCalendar.Header>
          <RangeCalendar.Grid>...</RangeCalendar.Grid>
        </DateRangePicker.Calendar>
      </DateRangePicker.Content>
    </DateRangePicker.Portal>
  </DateRangePicker.Select>
  <Description>...</Description>
</DateRangePicker>
  • DateRangePicker: Root container that manages date range selection state, open state, display formatting, and form field context (for Label, Description, FieldError). Supports controlled and uncontrolled modes.
  • DateRangePicker.Select: Pre-wired Select root connected to the DateRangePicker context. State props (value, isOpen, onValueChange, onOpenChange) are managed by the root.
  • DateRangePicker.Trigger: Pressable trigger button that opens the calendar overlay. Supports variant and inherits invalid border styling from the root.
  • DateRangePicker.Value: Text display for the selected range label. Shows a placeholder when no range is selected.
  • DateRangePicker.TriggerIndicator: Indicator icon inside the trigger. Defaults to a calendar icon instead of a chevron.
  • DateRangePicker.Portal: Portal wrapper that re-provides DateRangePicker context across the portal boundary.
  • DateRangePicker.Overlay: Backdrop overlay behind the calendar content.
  • DateRangePicker.Content: Content container for the calendar popup. Supports "popover", "dialog", and "bottom-sheet" presentations.
  • DateRangePicker.Calendar: Pre-wired RangeCalendar root that commits a completed range, updates the trigger label, and closes the overlay after selection. Two taps are required to complete a range (start, then end). Uses RangeCalendar compound parts as children.

Usage

Basic Usage

The DateRangePicker uses a popover presentation by default. Pass RangeCalendar compound parts as children of DateRangePicker.Calendar.

<DateRangePicker>
  <Label>Trip dates</Label>
  <DateRangePicker.Select>
    <DateRangePicker.Trigger>
      <DateRangePicker.Value />
      <DateRangePicker.TriggerIndicator />
    </DateRangePicker.Trigger>
    <DateRangePicker.Portal>
      <DateRangePicker.Overlay />
      <DateRangePicker.Content presentation="popover" width="trigger">
        <DateRangePicker.Calendar>
          <RangeCalendar.Header>
            <RangeCalendar.Heading />
            <RangeCalendar.NavButton slot="previous" />
            <RangeCalendar.NavButton slot="next" />
          </RangeCalendar.Header>
          <RangeCalendar.Grid>
            <RangeCalendar.GridHeader>
              {(day) => <RangeCalendar.HeaderCell day={day} />}
            </RangeCalendar.GridHeader>
            <RangeCalendar.GridBody>
              {(date) => <RangeCalendar.Cell date={date} />}
            </RangeCalendar.GridBody>
          </RangeCalendar.Grid>
        </DateRangePicker.Calendar>
      </DateRangePicker.Content>
    </DateRangePicker.Portal>
  </DateRangePicker.Select>
</DateRangePicker>

Dialog Presentation

Display the range calendar in a centered modal dialog.

<DateRangePicker>
  <Label>Trip dates</Label>
  <DateRangePicker.Select presentation="dialog">
    <DateRangePicker.Trigger>
      <DateRangePicker.Value />
      <DateRangePicker.TriggerIndicator />
    </DateRangePicker.Trigger>
    <DateRangePicker.Portal>
      <DateRangePicker.Overlay />
      <DateRangePicker.Content presentation="dialog">
        <DateRangePicker.Calendar>...</DateRangePicker.Calendar>
      </DateRangePicker.Content>
    </DateRangePicker.Portal>
  </DateRangePicker.Select>
</DateRangePicker>

Bottom Sheet Presentation

Display the range calendar in a bottom sheet.

<DateRangePicker>
  <Label>Trip dates</Label>
  <DateRangePicker.Select presentation="bottom-sheet">
    <DateRangePicker.Trigger>
      <DateRangePicker.Value />
      <DateRangePicker.TriggerIndicator />
    </DateRangePicker.Trigger>
    <DateRangePicker.Portal>
      <DateRangePicker.Overlay />
      <DateRangePicker.Content presentation="bottom-sheet">
        <DateRangePicker.Calendar>...</DateRangePicker.Calendar>
      </DateRangePicker.Content>
    </DateRangePicker.Portal>
  </DateRangePicker.Select>
</DateRangePicker>

Display Format

Configure how the selected range is displayed in the trigger using dateDisplayFormat.

<DateRangePicker dateDisplayFormat="long">
  <Label>Trip dates</Label>
  <DateRangePicker.Select>
    <DateRangePicker.Trigger>
      <DateRangePicker.Value />
      <DateRangePicker.TriggerIndicator />
    </DateRangePicker.Trigger>
    <DateRangePicker.Portal>
      <DateRangePicker.Overlay />
      <DateRangePicker.Content presentation="popover" width="trigger">
        <DateRangePicker.Calendar>...</DateRangePicker.Calendar>
      </DateRangePicker.Content>
    </DateRangePicker.Portal>
  </DateRangePicker.Select>
</DateRangePicker>

Custom Format Function

Override the display label entirely with formatDateRange.

function formatSpanishRange(start: CalendarDate, end: CalendarDate): string {
  const fmt = new Intl.DateTimeFormat('es-ES', {
    day: 'numeric',
    month: 'long',
    year: 'numeric',
  });
  const a = fmt.format(start.toDate(getLocalTimeZone()));
  const b = fmt.format(end.toDate(getLocalTimeZone()));
  if (start.compare(end) === 0) return a;
  return `${a} – ${b}`;
}

<DateRangePicker formatDateRange={formatSpanishRange} locale="es-ES">
  <Label>Custom range label</Label>
  <DateRangePicker.Select>
    <DateRangePicker.Trigger>
      <DateRangePicker.Value />
      <DateRangePicker.TriggerIndicator />
    </DateRangePicker.Trigger>
    <DateRangePicker.Portal>
      <DateRangePicker.Overlay />
      <DateRangePicker.Content presentation="popover" width="trigger">
        <DateRangePicker.Calendar>...</DateRangePicker.Calendar>
      </DateRangePicker.Content>
    </DateRangePicker.Portal>
  </DateRangePicker.Select>
  <Description>Pick a start date, then an end date.</Description>
</DateRangePicker>;

Custom Range Separator

Change the separator between formatted start and end dates in the trigger label.

<DateRangePicker rangeSeparator="to">
  <Label>Stay</Label>
  <DateRangePicker.Select>
    <DateRangePicker.Trigger>
      <DateRangePicker.Value />
      <DateRangePicker.TriggerIndicator />
    </DateRangePicker.Trigger>
    <DateRangePicker.Portal>
      <DateRangePicker.Overlay />
      <DateRangePicker.Content presentation="popover" width="trigger">
        <DateRangePicker.Calendar>...</DateRangePicker.Calendar>
      </DateRangePicker.Content>
    </DateRangePicker.Portal>
  </DateRangePicker.Select>
</DateRangePicker>

Field States

Use root props for required, invalid, and disabled states.

<DateRangePicker isRequired>
  <Label>Travel dates</Label>
  <DateRangePicker.Select>
    <DateRangePicker.Trigger>
      <DateRangePicker.Value placeholder="Select travel dates" />
      <DateRangePicker.TriggerIndicator />
    </DateRangePicker.Trigger>
    <DateRangePicker.Portal>
      <DateRangePicker.Overlay />
      <DateRangePicker.Content presentation="popover" width="trigger">
        <DateRangePicker.Calendar>...</DateRangePicker.Calendar>
      </DateRangePicker.Content>
    </DateRangePicker.Portal>
  </DateRangePicker.Select>
  <Description>Required for booking confirmation.</Description>
</DateRangePicker>

Example

import type { CalendarDate } from '@internationalized/date';
import { getLocalTimeZone } from '@internationalized/date';
import { Description, Label } from 'heroui-native';
import { DateRangePicker, RangeCalendar } from 'heroui-native-pro';
import { View } from 'react-native';

function formatSpanishDateRange(
  start: CalendarDate,
  end: CalendarDate
): string {
  const fmt = new Intl.DateTimeFormat('es-ES', {
    day: 'numeric',
    month: 'long',
    year: 'numeric',
  });
  const a = fmt.format(start.toDate(getLocalTimeZone()));
  const b = fmt.format(end.toDate(getLocalTimeZone()));
  if (start.compare(end) === 0) return a;
  return `${a} – ${b}`;
}

export default function DateRangePickerExample() {
  return (
    <View className="flex-1 justify-center px-5 gap-12">
      <DateRangePicker formatDateRange={formatSpanishDateRange} locale="es-ES">
        <Label>Custom range label (es-ES)</Label>
        <DateRangePicker.Select>
          <DateRangePicker.Trigger>
            <DateRangePicker.Value className="text-sm" numberOfLines={1} />
            <DateRangePicker.TriggerIndicator />
          </DateRangePicker.Trigger>
          <DateRangePicker.Portal>
            <DateRangePicker.Overlay />
            <DateRangePicker.Content presentation="popover" width="trigger">
              <DateRangePicker.Calendar>
                <RangeCalendar.Header>
                  <RangeCalendar.Heading />
                  <RangeCalendar.NavButton slot="previous" />
                  <RangeCalendar.NavButton slot="next" />
                </RangeCalendar.Header>
                <RangeCalendar.Grid>
                  <RangeCalendar.GridHeader>
                    {(day) => <RangeCalendar.HeaderCell day={day} />}
                  </RangeCalendar.GridHeader>
                  <RangeCalendar.GridBody>
                    {(date) => <RangeCalendar.Cell date={date} />}
                  </RangeCalendar.GridBody>
                </RangeCalendar.Grid>
              </DateRangePicker.Calendar>
            </DateRangePicker.Content>
          </DateRangePicker.Portal>
        </DateRangePicker.Select>
        <Description>Pick a start date, then an end date.</Description>
      </DateRangePicker>
    </View>
  );
}

API Reference

DateRangePicker

proptypedefaultdescription
childrenReact.ReactNode-Children elements (Label, DateRangePicker.Select, Description, FieldError)
valueDateRangePickerOption-Controlled selected option
defaultValueDateRangePickerOption-Default selected option for uncontrolled usage
isDisabledbooleanfalseWhether the entire field is disabled
isInvalidbooleanfalseWhether the field is in an invalid state
isRequiredbooleanfalseWhether the field is required
isOpenboolean-Controlled open state of the calendar overlay
isDefaultOpenboolean-Initial open state for uncontrolled usage
dateDisplayFormatDateRangePickerDateDisplayFormat'medium'Preset date label format; ignored when formatDateRange is set
localestring-BCP 47 locale for label formatting and calendar grid
formatDateRange(start: CalendarDate, end: CalendarDate) => string-Custom formatter that overrides dateDisplayFormat and locale for labels
rangeSeparatorstring'–'Separator between start and end dates when using presets; same-day ranges collapse to a single date
classNamestring-Additional CSS classes for the root container
animationAnimationRootDisableAll-Animation configuration for the date range picker subtree
onValueChange(value: DateRangePickerOption | undefined) => void-Handler called when the selected option changes
onOpenChange(open: boolean) => void-Handler called when the open state changes
...ViewPropsViewProps-All standard React Native View props are supported

DateRangePickerOption

propertytypedescription
valuestringJSON string encoding start/end ISO dates (e.g. {"start":"2026-04-01","end":"2026-04-07"})
labelstringDisplay string shown in the trigger (e.g. "Apr 1, 2026 – Apr 7, 2026")

DateRangePickerDateDisplayFormat

Built-in date label presets (maps to Intl.DateTimeFormat dateStyle):

  • 'short' — e.g. "4/1/26 – 4/7/26"
  • 'medium' — e.g. "Apr 1, 2026 – Apr 7, 2026" (default)
  • 'long' — e.g. "April 1, 2026 – April 7, 2026"
  • 'full' — e.g. "Wednesday, April 1, 2026 – Tuesday, April 7, 2026"

AnimationRootDisableAll

Animation configuration for the DateRangePicker root. Can be:

  • "disable-all": Disable all animations including children (cascades down)
  • undefined: Use default animations

DateRangePicker.Select

proptypedefaultdescription
childrenReact.ReactNode-Select content (Trigger, Portal)
isDisabledboolean-Overrides the root isDisabled when set
presentation'popover' | 'dialog' | 'bottom-sheet''popover'Presentation mode for the select content
classNamestring-Additional CSS classes
...ViewPropsViewProps-All standard React Native View props are supported

DateRangePicker.Trigger

proptypedefaultdescription
childrenReact.ReactNode-Trigger content (Value, TriggerIndicator)
variant'default' | 'unstyled''default'Trigger styling variant
isDisabledboolean-Whether the trigger is disabled
isInvalidboolean-When true, applies a danger border; inherits from root when omitted
classNamestring-Additional CSS classes for the trigger
...PressablePropsPressableProps-All standard React Native Pressable props are supported

DateRangePicker.Value

proptypedefaultdescription
placeholderstring'Choose a date range'Text shown when no range is selected
classNamestring-Additional CSS classes for the value text
...TextPropsTextProps-All standard React Native Text props are supported

DateRangePicker.TriggerIndicator

proptypedefaultdescription
childrenReact.ReactNode-Custom indicator content; defaults to a calendar icon when omitted
iconPropsSelectTriggerIndicatorIconProps-Overrides for the default icon
isAnimatedStyleActivebooleanfalseWhether animated rotation styles are applied
classNamestring-Additional CSS classes for the indicator container
animationSelectTriggerIndicatorAnimationfalseRotation animation configuration; disabled by default for calendar icon
...ViewPropsViewProps-All standard React Native View props are supported

SelectTriggerIndicatorIconProps

proptypedefaultdescription
sizenumber16Icon size in logical pixels
colorstringmutedIcon fill color

DateRangePicker.Portal

proptypedefaultdescription
childrenReact.ReactNode-Portal content (Overlay, Content)
hostNamestring-Optional name of the host element for the portal
disableFullWindowOverlaybooleanfalseUse a regular View instead of FullWindowOverlay on iOS
unstable_accessibilityContainerViewIsModalbooleanfalseControls whether VoiceOver treats the overlay as a modal container (iOS)
classNamestring-Additional CSS classes for the portal container

DateRangePicker.Overlay

proptypedefaultdescription
closeOnPressbooleantrueWhether to close the picker when the overlay is pressed
isAnimatedStyleActivebooleantrueWhether animated opacity styles are applied
classNamestring-Additional CSS classes for the overlay backdrop
animationSelectOverlayAnimation-Opacity animation configuration
...PressablePropsPressableProps-All standard React Native Pressable props are supported

DateRangePicker.Content

The content component is a union type based on the presentation prop.

Popover presentation

proptypedefaultdescription
childrenReact.ReactNode-Content (DateRangePicker.Calendar)
presentation'popover'-Popover presentation mode
width'content-fit' | 'trigger' | 'full' | number'content-fit'Content width sizing strategy
classNamestring-Additional CSS classes for the content container
animationSelectContentPopoverAnimation-Keyframe animation configuration for entering/exiting
...ViewPropsViewProps-All standard React Native View props are supported

Dialog presentation

proptypedefaultdescription
childrenReact.ReactNode-Content (DateRangePicker.Calendar)
presentation'dialog'-Dialog presentation mode
isSwipeablebooleantrueWhether the dialog can be swiped to dismiss
classNamestring-Additional CSS classes for the content container
animationSelectContentAnimation-Keyframe animation configuration for scale/opacity
...ViewPropsViewProps-All standard React Native View props are supported

Bottom sheet presentation

proptypedefaultdescription
childrenReact.ReactNode-Content (DateRangePicker.Calendar)
presentation'bottom-sheet'-Bottom sheet presentation mode
...BottomSheetPropsBottomSheetProps-All @gorhom/bottom-sheet props are supported

DateRangePicker.Calendar

proptypedefaultdescription
childrenReact.ReactNode-RangeCalendar compound parts (RangeCalendar.Header, RangeCalendar.Grid, etc.)
valueRangeValue<DateValue> | null-Overrides the calendar value derived from the root selection
localestring-Overrides the root locale for the calendar grid
accessibilityLabelstring'Pick a date range'Screen reader label for the calendar container
onChange(value: RangeValue<DateValue> | null) => void-Side-effect handler called for range updates including null while restarting selection
...RangeCalendarPropsRangeCalendarProps-All RangeCalendar root props are supported (minValue, maxValue, allowsNonContiguousRanges, etc.)

Hooks

useDateRangePicker

Hook to access the DateRangePicker context. Must be used within a DateRangePicker component.

import { useDateRangePicker } from 'heroui-native-pro';

const { value, commitRange, isOpen, formatRangeLabel } = useDateRangePicker();

Returns: DateRangePickerContextValue

propertytypedescription
valueDateRangePickerOption | undefinedCurrent select option (JSON range string + display label)
onValueChange(next: DateRangePickerOption | undefined) => voidUpdate the selected option
isOpenbooleanWhether the calendar overlay is open
onOpenChange(open: boolean) => voidUpdate the open state
commitRange(range: RangeValue<CalendarDate>) => voidCommit a range: updates the option, formats the label, closes the overlay
formatRangeLabel(start: CalendarDate, end: CalendarDate) => stringFormat a range using root dateDisplayFormat / locale / formatDateRange
isDisabledRootbooleanWhether the root is disabled
localestring | undefinedRoot locale forwarded to DateRangePicker.Calendar

On this page