File size: 7,336 Bytes
cf86710 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | ---
sidebar_position: 1
---
# Input Fields
Binding a DayPicker to an input field requires handling user interactions, synchronizing the selected date between the calendar and the field, and maintaining a good level of usability.
:::info Native Date Pickers
Browsers offer [native date pickers](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date) for easy date selection. However, their appearance and functionality vary across browsers and might not meet your customization needs. Use DayPicker for a tailored date picker that aligns with your app's design and accessibility standards.
:::
## Examples
### Input with Inline Calendar
See the [full source code](https://github.com/gpbl/react-day-picker/blob/main/examples/Input.tsx) and [unit tests](https://github.com/gpbl/react-day-picker/blob/main/examples/Input.test.tsx) for this example.
<BrowserWindow sourceUrl="https://github.com/gpbl/react-day-picker/blob/main/examples/Input.tsx">
<Examples.Input />
</BrowserWindow>
```tsx
import { useId, useState } from "react";
import { format, isValid, parse } from "date-fns";
import { DayPicker } from "react-day-picker";
/** Render an input field bound to a DayPicker calendar. */
export function Input() {
const inputId = useId();
// Hold the month in state to control the calendar when the input changes
const [month, setMonth] = useState(new Date());
// Hold the selected date in state
const [selectedDate, setSelectedDate] = useState<Date | undefined>(undefined);
// Hold the input value in state
const [inputValue, setInputValue] = useState("");
const handleDayPickerSelect = (date: Date | undefined) => {
if (!date) {
setInputValue("");
setSelectedDate(undefined);
} else {
setSelectedDate(date);
setMonth(date);
setInputValue(format(date, "MM/dd/yyyy"));
}
};
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value); // Keep the input value in sync
const parsedDate = parse(e.target.value, "MM/dd/yyyy", new Date());
if (isValid(parsedDate)) {
setSelectedDate(parsedDate);
setMonth(parsedDate);
} else {
setSelectedDate(undefined);
}
};
return (
<div>
<label htmlFor={inputId}>
<strong>Date: </strong>
</label>
<input
style={{ fontSize: "inherit" }}
id={inputId}
type="text"
value={inputValue}
placeholder="MM/dd/yyyy"
onChange={handleInputChange}
/>
<DayPicker
month={month}
onMonthChange={setMonth}
mode="single"
selected={selectedDate}
onSelect={handleDayPickerSelect}
footer={`Selected: ${selectedDate?.toDateString()}`}
/>
</div>
);
}
```
### Input with Date Picker Dialog
Implementing the date picker as a dialog requires careful consideration of accessibility. Refer to the [W3C ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/examples/datepicker-dialog/) for guidance on creating an accessible date picker.
In this example, we use the native HTML `<dialog>` element, which provides a built-in way to create a modal dialog. You can replace the native `<dialog>` element with a custom dialog component or a modal library that fits your application's design and accessibility requirements.
If a screen reader keeps the arrow keys for browse mode (for example, NVDA), set `role="application"` on the calendar root and make sure it has an accessible name that includes the current month (for example, "Calendar, September 2025"). This prompts focus mode so arrow-key navigation works inside the calendar.
<BrowserWindow sourceUrl="https://github.com/gpbl/react-day-picker/blob/main/examples/Dialog.tsx">
<Examples.Dialog />
</BrowserWindow>
```tsx
import { useEffect, useId, useRef, useState } from "react";
import { format, isValid, parse } from "date-fns";
import { DayPicker } from "react-day-picker";
export function Dialog() {
const dialogRef = useRef<HTMLDialogElement>(null);
const dialogId = useId();
const headerId = useId();
// Hold the month in state to control the calendar when the input changes
const [month, setMonth] = useState(new Date());
// Hold the selected date in state
const [selectedDate, setSelectedDate] = useState<Date | undefined>(undefined);
// Hold the input value in state
const [inputValue, setInputValue] = useState("");
// Hold the dialog visibility in state
const [isDialogOpen, setIsDialogOpen] = useState(false);
// Function to toggle the dialog visibility
const toggleDialog = () => setIsDialogOpen(!isDialogOpen);
const calendarLabel = `Calendar, ${format(month, "MMMM yyyy")}`;
// Hook to handle the body scroll behavior and focus trapping
useEffect(() => {
const handleBodyScroll = (isOpen: boolean) => {
document.body.style.overflow = isOpen ? "hidden" : "";
};
if (!dialogRef.current) return;
if (isDialogOpen) {
handleBodyScroll(true);
dialogRef.current.showModal();
} else {
handleBodyScroll(false);
dialogRef.current.close();
}
return () => {
handleBodyScroll(false);
};
}, [isDialogOpen]);
const handleDayPickerSelect = (date: Date | undefined) => {
if (!date) {
setInputValue("");
setSelectedDate(undefined);
} else {
setSelectedDate(date);
setInputValue(format(date, "MM/dd/yyyy"));
}
dialogRef.current?.close();
};
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value); // Keep the input value in sync
const parsedDate = parse(e.target.value, "MM/dd/yyyy", new Date());
if (isValid(parsedDate)) {
setSelectedDate(parsedDate);
setMonth(parsedDate);
} else {
setSelectedDate(undefined);
}
};
return (
<div>
<label htmlFor="date-input">
<strong>Pick a Date: </strong>
</label>
<input
style={{ fontSize: "inherit" }}
id="date-input"
type="text"
value={inputValue}
placeholder="MM/dd/yyyy"
onChange={handleInputChange}
/>{" "}
<button
style={{ fontSize: "inherit" }}
onClick={toggleDialog}
aria-controls={dialogId}
aria-haspopup="dialog"
aria-expanded={isDialogOpen}
aria-label="Open calendar to choose booking date"
>
📆
</button>
<p aria-live="assertive" aria-atomic="true">
{selectedDate !== undefined
? selectedDate.toDateString()
: "Please type or pick a date"}
</p>
<dialog
role="dialog"
ref={dialogRef}
id={dialogId}
aria-modal
aria-labelledby={headerId}
onClose={() => setIsDialogOpen(false)}
>
<h2 id={headerId}>Choose a date</h2>
<DayPicker
month={month}
onMonthChange={setMonth}
autoFocus
role="application"
aria-label={calendarLabel}
mode="single"
selected={selectedDate}
onSelect={handleDayPickerSelect}
footer={
selectedDate !== undefined &&
`Selected: ${selectedDate.toDateString()}`
}
/>
</dialog>
</div>
);
}
```
|