useDateRangePicker

Hook for date range selection with dual-month calendar, hover preview, and preset ranges.

Import

import { useDateRangePicker } from "react-date-range-picker-headless";

Usage

import { useState } from "react";
import { useDateRangePicker } from "react-date-range-picker-headless";
function MyDateRangePicker() {
const [range, setRange] = useState<{ start: Date | null; end: Date | null }>({
start: null,
end: null,
});
const picker = useDateRangePicker({
value: range,
onChange: setRange,
presets: [
{
label: "Last 7 days",
value: () => {
const end = new Date();
const start = new Date();
start.setDate(start.getDate() - 6);
return { start, end };
},
},
],
});
return (
<div ref={picker.containerRef}>
<button onClick={picker.handleToggle}>
{picker.displayValue || picker.locale.rangePlaceholder}
</button>
{picker.isOpen && (
<div ref={picker.popupRef} onKeyDown={picker.handleKeyDown}>
{/* Two calendars side by side */}
{[picker.leftCalendar, picker.rightCalendar].map((cal, calIdx) => (
<div key={calIdx}>
<span>{picker.locale.formatMonthYear(cal.month)}</span>
{cal.weeks.flat().map((day, i) => {
if (!day) return <span key={i} />;
const dp = picker.getDayProps(day, cal.month);
return (
<button
key={i}
onClick={() => picker.handleDateClick(day)}
onMouseEnter={() => picker.handleDateHover(day)}
onMouseLeave={() => picker.handleDateHover(null)}
>
{dp.day}
</button>
);
})}
</div>
))}
{/* Presets */}
{picker.presets.map((preset, i) => (
<button
key={i}
onClick={() => picker.handlePresetClick(preset)}
style={{ fontWeight: i === picker.activePresetIndex ? "bold" : "normal" }}
>
{preset.label}
</button>
))}
<button onClick={picker.handleConfirm}>{picker.locale.confirm}</button>
</div>
)}
</div>
);
}

Options

OptionTypeDefaultDescription
value{ start: Date | null; end: Date | null }Required. Current range value.
onChange(value: { start: Date | null; end: Date | null }) => voidRequired. Called when the range changes.
maxDaysnumberMaximum number of days allowed in a range (inclusive).
minDaysnumberMinimum number of days required in a range (inclusive).
presetsDateRangePreset[]Predefined date range presets (e.g. “Last 7 days”, “This month”).
allowSingleDateInRangebooleantrueWhen false, prevents selecting a range where start equals end.
minDateDateEarliest selectable date.
maxDateDateLatest selectable date.
localePartial<Locale>DEFAULT_LOCALEOverride locale strings.
initialMonthDateInitial month to display.
sizeDatePickerSizeUI pass-through.
weekStartsOnWeekDay"sunday"First day of the week.
isDateUnavailable(date: Date) => booleanCustom function to disable specific dates.
displayFormatstringCustom format string for the display value.
openbooleanControlled open state.
initialOpenbooleanfalseInitial open state (uncontrolled).
onOpenChange(open: boolean) => voidCallback when open state changes.
requiredbooleanfalseWhen true, handleClear is a no-op.
todayDatenew Date()Override today’s date.
onMonthChange(month: Date) => voidCallback when the displayed month changes.
disablePastbooleanfalseDisable all dates before today.
disableFuturebooleanfalseDisable all dates after today.
showOutsideDaysbooleanfalseShow adjacent month days.
highlightDatesDate[]Array of dates to highlight.
shouldCloseOnSelectbooleanfalseAuto-confirm when end date is selected.
numberOfMonthsnumber2Number of calendar months to display.
captionLayoutCaptionLayout"buttons"Caption layout mode.
fromYearnumbercurrent year - 100Start year for dropdown.
toYearnumbercurrent year + 10End year for dropdown.

Return Values

NameTypeDescription
isOpenbooleanWhether the popup is open.
tempStartDateDate | nullTemporary start date (before confirmation).
tempEndDateDate | nullTemporary end date (before confirmation).
hoveredDateDate | nullCurrently hovered date (for range preview).
leftMonthDateLeft calendar’s displayed month.
rightMonthDateRight calendar’s displayed month.
localeLocaleResolved locale object.
leftCalendarCalendarMonthCalendar data for the left panel.
rightCalendarCalendarMonthCalendar data for the right panel.
calendarsCalendarMonth[]Array of all calendar months.
getDayProps(date: Date, referenceMonth?: Date) => DayPropsCompute flags for a calendar day cell. Pass referenceMonth so outside-day detection works correctly in multi-month layouts.
displayValuestringFormatted string of the confirmed range.
hasValuebooleanWhether a range is currently confirmed.
canConfirmbooleanWhether the current selection is valid for confirmation.
handleDateClick(date: Date) => voidHandle a day cell click. First click sets start, second sets end.
handleDateHover(date: Date | null) => voidHandle mouse hover for range preview.
handlePrevMonth() => voidNavigate both calendars one month back.
handleNextMonth() => voidNavigate both calendars one month forward.
handleOpen() => voidOpen the popup.
handleClose() => voidClose the popup.
handleToggle() => voidToggle the popup.
handleConfirm() => voidConfirm the selection and close.
handleCancel() => voidCancel and revert to previous value.
handleClear() => voidClear the range (no-op if required).
handleGoToToday() => voidNavigate to today’s month.
containerRefRefObject<HTMLDivElement | null>Attach to wrapper for click-outside detection.
popupRefRefObject<HTMLDivElement | null>Attach to the popup element.
focusedDateDate | nullKeyboard-focused date.
handleKeyDown(e: KeyboardEvent<HTMLElement>) => voidKeyboard navigation handler.
presetsDateRangePreset[]The presets array (passthrough for rendering).
handlePresetClick(preset: DateRangePreset) => voidApply a preset range.
activePresetIndexnumberIndex of the currently matching preset (-1 if none).
yearsnumber[]Years array for dropdown mode.
monthsnumber[]Month indices for dropdown mode.
handleYearSelect(year: number, calendarIndex?: number) => voidSet displayed year.
handleMonthSelect(month: number, calendarIndex?: number) => voidSet displayed month.

Key Behaviors

Range Selection Flow

  1. First click sets the start date (end date clears)
  2. Second click sets the end date (auto-swaps if before start)
  3. Third click resets and starts a new range

Hover Preview

While the user has selected a start date but not yet an end date, handleDateHover updates hoveredDate. The getDayProps function uses this to compute isInHoverRange and isHoverTarget, allowing you to show a preview of the potential range.

Presets

Presets can be static objects or factory functions:

const presets = [
{ label: "Today", value: { start: new Date(), end: new Date() } },
{
label: "Last 30 days",
value: () => {
const end = new Date();
const start = new Date();
start.setDate(start.getDate() - 29);
return { start, end };
},
},
];

activePresetIndex returns the index of the preset that matches the current selection, or -1 if none match.

maxDays / minDays

When maxDays is set, dates beyond the allowed range from the start date are automatically disabled. When minDays is set, dates too close to the start date are disabled.

shouldCloseOnSelect

For range pickers, shouldCloseOnSelect triggers auto-confirm when the end date is selected (not the start date).