Custom Selections
If the built-in selection modes do not satisfy your app’s requirements, you can control the selection behavior using the onDayClick and modifiers props.
| Prop | Type | Description |
|---|---|---|
onDayClick | DayEventHandler | Callback when a day is clicked. |
modifiers | Modifiers | An object with the modifiers of the days. |
The onDayClick prop is a function that receives the clicked day and its modifiers as arguments.
How custom selection works
- Omit
mode(or set it toundefined) to disable built-in selection logic. - Keep your own selection state, derive a
selectedmodifier (and anyrange_*modifiers) from it, and pass them tomodifiersso styling/ARIA stay aligned. - Ignore clicks on
modifiers.disabledormodifiers.hiddento preserve accessibility. - For keyboard support, handle
onDayKeyDown(Space/Enter) in the same way asonDayClick.
Examples
Week Selection
The following example demonstrates how to select an entire week when a day is clicked.
Note the use of the startOfWeek and endOfWeek functions from date-fns, and how the selectedWeek state is passed to the modifiers prop.
import React, { useState } from "react";
import { endOfWeek, startOfWeek } from "date-fns";
import { type DateRange, DayPicker, rangeIncludesDate } from "react-day-picker";
/** Select the whole week when the day is clicked. */
export function CustomWeek() {
const [selectedWeek, setSelectedWeek] = useState<DateRange | undefined>();
return (
<DayPicker
showWeekNumber
showOutsideDays
modifiers={{
selected: selectedWeek,
range_start: selectedWeek?.from,
range_end: selectedWeek?.to,
range_middle: (date: Date) =>
selectedWeek ? rangeIncludesDate(selectedWeek, date, true) : false,
}}
onDayClick={(day, modifiers) => {
if (modifiers.disabled || modifiers.hidden) return;
if (modifiers.selected) {
setSelectedWeek(undefined);
return;
}
setSelectedWeek({
from: startOfWeek(day),
to: endOfWeek(day),
});
}}
onDayKeyDown={(day, modifiers, e) => {
if (e.key === " " || e.key === "Enter") {
e.preventDefault();
if (modifiers.disabled || modifiers.hidden) return;
if (modifiers.selected) {
setSelectedWeek(undefined);
return;
}
setSelectedWeek({
from: startOfWeek(day),
to: endOfWeek(day),
});
}
}}
footer={
selectedWeek &&
`Week from ${selectedWeek?.from?.toLocaleDateString()} to ${selectedWeek?.to?.toLocaleDateString()}`
}
/>
);
}
Single Selection
For example, to implement the "single selection" behavior (similar to when mode="single"):
import { useState } from "react";
import { DayPicker, DayPickerProps } from "react-day-picker";
export function CustomSingle() {
const [selectedDate, setSelectedDate] = useState<Date | undefined>();
const modifiers: DayPickerProps["modifiers"] = {};
if (selectedDate) {
modifiers.selected = selectedDate;
}
return (
<DayPicker
modifiers={modifiers}
onDayClick={(day, modifiers) => {
if (modifiers.disabled || modifiers.hidden) return;
setSelectedDate(modifiers.selected ? undefined : day);
}}
onDayKeyDown={(day, modifiers, e) => {
if (e.key === " " || e.key === "Enter") {
e.preventDefault();
if (modifiers.disabled || modifiers.hidden) return;
setSelectedDate(modifiers.selected ? undefined : day);
}
}}
footer={selectedDate && `You selected ${selectedDate.toDateString()}`}
/>
);
}
Multi Selections
Selecting multiple days is a bit more complex as it involves handling an array of dates. The following example replicates the mode="multiple" selection mode:
import { useState } from "react";
import { isSameDay } from "date-fns";
import { DayEventHandler, DayPicker } from "react-day-picker";
export function CustomMultiple() {
const [value, setValue] = useState<Date[]>([]);
const handleDayClick: DayEventHandler<React.MouseEvent> = (
day,
modifiers,
) => {
if (modifiers.disabled || modifiers.hidden) return;
const newValue = [...value];
if (modifiers.selected) {
const index = value.findIndex((d) => isSameDay(day, d));
newValue.splice(index, 1);
} else {
if (!value.some((d) => isSameDay(d, day))) {
newValue.push(day);
}
}
setValue(newValue);
};
const handleResetClick = () => setValue([]);
let footer = <>Please pick one or more days.</>;
if (value.length > 0)
footer = (
<>
You selected {value.length} days.{" "}
<button type="button" onClick={handleResetClick}>
Reset
</button>
</>
);
return (
<DayPicker
onDayClick={handleDayClick}
onDayKeyDown={(day, modifiers, e) => {
if (e.key === " " || e.key === "Enter") {
e.preventDefault();
handleDayClick(day, modifiers, e as any);
}
}}
modifiers={{ selected: value }}
footer={footer}
/>
);
}
Rolling N-day window
Create a fixed-length range (for example, 7 days) starting from the clicked day.
import { addDays } from "date-fns";
import { useState } from "react";
import { type DateRange, DayPicker } from "react-day-picker";
export function CustomRollingWindow() {
const [range, setRange] = useState<DateRange | undefined>();
const windowLength = 7;
const applyRange = (start: Date): DateRange => ({
from: start,
to: addDays(start, windowLength - 1),
});
return (
<DayPicker
modifiers={{
selected: range,
range_start: range?.from,
range_end: range?.to,
range_middle: range,
}}
onDayClick={(day, modifiers) => {
if (modifiers.disabled || modifiers.hidden) return;
setRange(modifiers.selected ? undefined : applyRange(day));
}}
onDayKeyDown={(day, modifiers, e) => {
if (e.key === " " || e.key === "Enter") {
e.preventDefault();
if (modifiers.disabled || modifiers.hidden) return;
setRange(modifiers.selected ? undefined : applyRange(day));
}
}}
/>
);
}
Selecting full months
Toggle an entire month at a time.
import { endOfMonth, startOfMonth } from "date-fns";
import { useState } from "react";
import { type DateRange, DayPicker } from "react-day-picker";
export function CustomMonthSelection() {
const [monthRange, setMonthRange] = useState<DateRange | undefined>();
const toMonthRange = (day: Date): DateRange => ({
from: startOfMonth(day),
to: endOfMonth(day),
});
const isInRange = (day: Date) =>
monthRange?.from && monthRange?.to
? day >= monthRange.from && day <= monthRange.to
: false;
return (
<DayPicker
showOutsideDays
modifiers={{
selected: monthRange,
range_start: monthRange?.from,
range_end: monthRange?.to,
range_middle: monthRange,
}}
onDayClick={(day, modifiers) => {
if (modifiers.disabled || modifiers.hidden) return;
setMonthRange(isInRange(day) ? undefined : toMonthRange(day));
}}
/>
);
}
Style custom modifiers
Use modifiersClassNames or modifiersStyles to surface your custom state (for example, range_middle, rolling, blocked).
<DayPicker
modifiers={{ selected: value, rolling: rollingRange }}
modifiersClassNames={{ rolling: "my-rolling" }}
modifiersStyles={{ rolling: { background: "var(--rdp-accent-color)" } }}
/>
Week numbers and starts
For week-based selections, set weekStartsOn (locale-aware) and consider handling onWeekNumberClick to select a week when its number is clicked. Combine it with showWeekNumber.
<DayPicker
showWeekNumber
weekStartsOn={1}
onWeekNumberClick={(weekNumber, week, e) => {
e.preventDefault();
setSelectedWeek({ from: week.from, to: week.to });
}}
modifiers={{ selected: selectedWeek }}
/>
Persisting custom selections
Serialize your custom selection state before storing or sending it.
// Single date
const payload = selectedDate ? selectedDate.toISOString() : null;
// Multiple dates
const payload = value.map((date) => date.toISOString());
// Range
const payload =
range?.from && range?.to
? { from: range.from.toISOString(), to: range.to.toISOString() }
: null;