0
# Calendar Controllers
1
2
Headless calendar components that provide date selection functionality without input fields. These are ideal for custom implementations, embedded calendars, and advanced use cases where you need full control over the user interface.
3
4
## Capabilities
5
6
### DayPickerRangeController
7
8
Calendar-only component for date range selection without input fields. Perfect for building custom date range interfaces or embedded calendar displays.
9
10
```jsx { .api }
11
/**
12
* Headless date range calendar controller without input fields
13
* @param props - DayPickerRangeController configuration
14
* @returns Calendar component for date range selection
15
*/
16
function DayPickerRangeController(props: DayPickerRangeControllerProps): ReactElement;
17
18
interface DayPickerRangeControllerProps {
19
// Date state
20
startDate?: moment.Moment | null;
21
endDate?: moment.Moment | null;
22
focusedInput?: 'startDate' | 'endDate' | null;
23
24
// Required callbacks
25
onDatesChange: ({ startDate, endDate }: {
26
startDate: moment.Moment | null;
27
endDate: moment.Moment | null;
28
}) => void;
29
onFocusChange: (focusedInput: 'startDate' | 'endDate' | null) => void;
30
31
// Calendar presentation
32
orientation?: 'horizontal' | 'vertical';
33
numberOfMonths?: number;
34
enableOutsideDays?: boolean;
35
daySize?: number;
36
isRTL?: boolean;
37
firstDayOfWeek?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
38
verticalHeight?: number;
39
transitionDuration?: number;
40
verticalSpacing?: number;
41
horizontalMonthPadding?: number;
42
43
// Navigation
44
navPosition?: 'navPositionTop' | 'navPositionBottom';
45
navPrev?: ReactNode;
46
navNext?: ReactNode;
47
renderNavPrevButton?: (props: any) => ReactNode;
48
renderNavNextButton?: (props: any) => ReactNode;
49
onPrevMonthClick?: (newMonth: moment.Moment) => void;
50
onNextMonthClick?: (newMonth: moment.Moment) => void;
51
52
// Day customization
53
renderCalendarDay?: (props: any) => ReactNode;
54
renderDayContents?: (day: moment.Moment, modifiers: Set<string>) => ReactNode;
55
renderMonthElement?: (props: any) => ReactNode;
56
renderMonthText?: (month: moment.Moment) => ReactNode;
57
renderWeekHeaderElement?: (day: string) => ReactNode;
58
minimumNights?: number;
59
isDayBlocked?: (day: moment.Moment) => boolean;
60
isOutsideRange?: (day: moment.Moment) => boolean;
61
isDayHighlighted?: (day: moment.Moment) => boolean;
62
63
// Interaction
64
keepOpenOnDateSelect?: boolean;
65
onDayClick?: (day: moment.Moment, event: SyntheticEvent) => void;
66
onDayMouseEnter?: (day: moment.Moment, event: SyntheticEvent) => void;
67
onDayMouseLeave?: (day: moment.Moment, event: SyntheticEvent) => void;
68
69
// Internationalization
70
monthFormat?: string;
71
weekDayFormat?: string;
72
phrases?: object;
73
dayAriaLabelFormat?: string;
74
75
// Initial state
76
initialVisibleMonth?: () => moment.Moment;
77
78
// Focus management
79
isFocused?: boolean;
80
showKeyboardShortcuts?: boolean;
81
onBlur?: () => void;
82
onTab?: (event: SyntheticEvent) => void;
83
onShiftTab?: (event: SyntheticEvent) => void;
84
}
85
```
86
87
**Usage Examples:**
88
89
```jsx
90
import React, { useState } from "react";
91
import { DayPickerRangeController } from "react-dates";
92
import moment from "moment";
93
94
// Basic embedded calendar
95
function EmbeddedRangeCalendar() {
96
const [startDate, setStartDate] = useState(null);
97
const [endDate, setEndDate] = useState(null);
98
const [focusedInput, setFocusedInput] = useState('startDate');
99
100
return (
101
<div style={{ width: '100%', maxWidth: 600 }}>
102
<DayPickerRangeController
103
startDate={startDate}
104
endDate={endDate}
105
onDatesChange={({ startDate, endDate }) => {
106
setStartDate(startDate);
107
setEndDate(endDate);
108
}}
109
focusedInput={focusedInput}
110
onFocusChange={setFocusedInput}
111
numberOfMonths={2}
112
minimumNights={1}
113
/>
114
</div>
115
);
116
}
117
118
// Custom calendar with business logic
119
function BookingCalendar({ unavailableDates, rates }) {
120
const [startDate, setStartDate] = useState(null);
121
const [endDate, setEndDate] = useState(null);
122
const [focusedInput, setFocusedInput] = useState('startDate');
123
124
const isDayBlocked = (day) => {
125
return unavailableDates.some(blockedDay =>
126
moment(blockedDay).isSame(day, 'day')
127
);
128
};
129
130
const renderDayContents = (day, modifiers) => {
131
const rate = rates[day.format('YYYY-MM-DD')];
132
return (
133
<div>
134
<div>{day.format('D')}</div>
135
{rate && <div style={{ fontSize: '10px' }}>${rate}</div>}
136
</div>
137
);
138
};
139
140
return (
141
<div>
142
<DayPickerRangeController
143
startDate={startDate}
144
endDate={endDate}
145
onDatesChange={({ startDate, endDate }) => {
146
setStartDate(startDate);
147
setEndDate(endDate);
148
149
// Custom business logic
150
if (startDate && endDate) {
151
console.log('Total nights:', endDate.diff(startDate, 'days'));
152
}
153
}}
154
focusedInput={focusedInput}
155
onFocusChange={setFocusedInput}
156
isDayBlocked={isDayBlocked}
157
renderDayContents={renderDayContents}
158
minimumNights={2}
159
numberOfMonths={3}
160
/>
161
</div>
162
);
163
}
164
```
165
166
### DayPickerSingleDateController
167
168
Calendar-only component for single date selection without input fields. Ideal for embedded date pickers and custom interfaces.
169
170
```jsx { .api }
171
/**
172
* Headless single date calendar controller without input fields
173
* @param props - DayPickerSingleDateController configuration
174
* @returns Calendar component for single date selection
175
*/
176
function DayPickerSingleDateController(props: DayPickerSingleDateControllerProps): ReactElement;
177
178
interface DayPickerSingleDateControllerProps {
179
// Date state
180
date?: moment.Moment | null;
181
focused?: boolean;
182
183
// Required callbacks
184
onDateChange: (date: moment.Moment | null) => void;
185
onFocusChange: ({ focused }: { focused: boolean }) => void;
186
187
// Calendar presentation
188
orientation?: 'horizontal' | 'vertical';
189
numberOfMonths?: number;
190
enableOutsideDays?: boolean;
191
daySize?: number;
192
isRTL?: boolean;
193
firstDayOfWeek?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
194
verticalHeight?: number;
195
transitionDuration?: number;
196
verticalSpacing?: number;
197
horizontalMonthPadding?: number;
198
199
// Navigation
200
navPosition?: 'navPositionTop' | 'navPositionBottom';
201
navPrev?: ReactNode;
202
navNext?: ReactNode;
203
renderNavPrevButton?: (props: any) => ReactNode;
204
renderNavNextButton?: (props: any) => ReactNode;
205
onPrevMonthClick?: (newMonth: moment.Moment) => void;
206
onNextMonthClick?: (newMonth: moment.Moment) => void;
207
208
// Day customization
209
renderCalendarDay?: (props: any) => ReactNode;
210
renderDayContents?: (day: moment.Moment, modifiers: Set<string>) => ReactNode;
211
renderMonthElement?: (props: any) => ReactNode;
212
renderMonthText?: (month: moment.Moment) => ReactNode;
213
renderWeekHeaderElement?: (day: string) => ReactNode;
214
isDayBlocked?: (day: moment.Moment) => boolean;
215
isOutsideRange?: (day: moment.Moment) => boolean;
216
isDayHighlighted?: (day: moment.Moment) => boolean;
217
218
// Interaction
219
keepOpenOnDateSelect?: boolean;
220
onDayClick?: (day: moment.Moment, event: SyntheticEvent) => void;
221
onDayMouseEnter?: (day: moment.Moment, event: SyntheticEvent) => void;
222
onDayMouseLeave?: (day: moment.Moment, event: SyntheticEvent) => void;
223
224
// Internationalization
225
monthFormat?: string;
226
weekDayFormat?: string;
227
phrases?: object;
228
dayAriaLabelFormat?: string;
229
230
// Initial state
231
initialVisibleMonth?: () => moment.Moment;
232
233
// Focus management
234
isFocused?: boolean;
235
showKeyboardShortcuts?: boolean;
236
onBlur?: () => void;
237
onTab?: (event: SyntheticEvent) => void;
238
onShiftTab?: (event: SyntheticEvent) => void;
239
}
240
```
241
242
**Usage Examples:**
243
244
```jsx
245
import React, { useState } from "react";
246
import { DayPickerSingleDateController } from "react-dates";
247
import moment from "moment";
248
249
// Simple embedded calendar
250
function EmbeddedSingleCalendar() {
251
const [date, setDate] = useState(null);
252
const [focused, setFocused] = useState(true);
253
254
return (
255
<div style={{ width: 300 }}>
256
<DayPickerSingleDateController
257
date={date}
258
onDateChange={setDate}
259
focused={focused}
260
onFocusChange={({ focused }) => setFocused(focused)}
261
numberOfMonths={1}
262
/>
263
</div>
264
);
265
}
266
267
// Appointment scheduler calendar
268
function AppointmentCalendar({ availableSlots, onDateSelect }) {
269
const [selectedDate, setSelectedDate] = useState(null);
270
const [focused, setFocused] = useState(true);
271
272
const isDayBlocked = (day) => {
273
// Block days without available slots
274
const dayKey = day.format('YYYY-MM-DD');
275
return !availableSlots[dayKey] || availableSlots[dayKey].length === 0;
276
};
277
278
const renderDayContents = (day, modifiers) => {
279
const dayKey = day.format('YYYY-MM-DD');
280
const slots = availableSlots[dayKey];
281
const availableCount = slots ? slots.length : 0;
282
283
return (
284
<div style={{ textAlign: 'center' }}>
285
<div>{day.format('D')}</div>
286
{availableCount > 0 && (
287
<div style={{ fontSize: '10px', color: 'green' }}>
288
{availableCount} slots
289
</div>
290
)}
291
</div>
292
);
293
};
294
295
const handleDateChange = (date) => {
296
setSelectedDate(date);
297
onDateSelect(date);
298
};
299
300
return (
301
<div>
302
<h3>Select Appointment Date</h3>
303
<DayPickerSingleDateController
304
date={selectedDate}
305
onDateChange={handleDateChange}
306
focused={focused}
307
onFocusChange={({ focused }) => setFocused(focused)}
308
isDayBlocked={isDayBlocked}
309
renderDayContents={renderDayContents}
310
isOutsideRange={(day) => moment().diff(day) > 0} // No past dates
311
numberOfMonths={2}
312
/>
313
</div>
314
);
315
}
316
```
317
318
## Public Methods
319
320
Both controller components expose these public methods via refs:
321
322
```jsx { .api }
323
// Navigation methods
324
onDayClick(day: moment.Moment, event: SyntheticEvent): void;
325
onDayMouseEnter(day: moment.Moment, event?: SyntheticEvent): void;
326
onDayMouseLeave(day: moment.Moment, event?: SyntheticEvent): void;
327
onPrevMonthClick(newMonth?: moment.Moment): void;
328
onNextMonthClick(newMonth?: moment.Moment): void;
329
330
// Focus management
331
getFirstFocusableDay(newMonth: moment.Moment): moment.Moment;
332
```
333
334
## Common Patterns
335
336
### Custom Date Validation
337
338
```jsx
339
// Combined validation function
340
const isDateUnavailable = (day) => {
341
const isPastDate = moment().diff(day) > 0;
342
const isWeekend = day.day() === 0 || day.day() === 6;
343
const isHoliday = holidays.includes(day.format('YYYY-MM-DD'));
344
345
return isPastDate || isWeekend || isHoliday;
346
};
347
348
// Usage
349
<DayPickerSingleDateController
350
isDayBlocked={isDateUnavailable}
351
// ... other props
352
/>
353
```
354
355
### Custom Day Rendering
356
357
```jsx
358
// Complex day rendering with multiple data types
359
const renderDayContents = (day, modifiers) => {
360
const hasEvents = events[day.format('YYYY-MM-DD')];
361
const isHighlighted = modifiers.has('highlighted');
362
363
return (
364
<div className={`custom-day ${isHighlighted ? 'highlighted' : ''}`}>
365
<span className="day-number">{day.format('D')}</span>
366
{hasEvents && <div className="event-indicator" />}
367
</div>
368
);
369
};
370
```
371
372
### Month Navigation Handling
373
374
```jsx
375
// Custom month navigation with logging
376
const handlePrevMonth = (newMonth) => {
377
console.log('Navigated to:', newMonth.format('MMMM YYYY'));
378
// Custom logic here
379
};
380
381
const handleNextMonth = (newMonth) => {
382
console.log('Navigated to:', newMonth.format('MMMM YYYY'));
383
// Custom logic here
384
};
385
```