Skip to main content

Input Fields

It is a common scenario to bind the date picker with an input field. Since these kind of implementations may have different requirements, DayPicker doesn't come with an input field component.

It is easy to build a custom input field that works together with DayPicker:

  1. when the input field changes, parse its value and set the selected day in DayPicker
  2. when a day is selected from DayPicker, set the input value formatting the selected date.

Example: Date Picker Dialog

Implement a DayPicker dialog according to the WAI-ARIA’s dialog pattern for date pickers.

The following example uses react-popper for the popover.

|
import React, { ChangeEventHandler, useRef, useState } from 'react';

import { format, isValid, parse } from 'date-fns';
import FocusTrap from 'focus-trap-react';
import { DayPicker, SelectSingleEventHandler } from 'react-day-picker';
import { usePopper } from 'react-popper';

export default function DatePickerDialog() {
const [selected, setSelected] = useState<Date>();
const [inputValue, setInputValue] = useState<string>('');
const [isPopperOpen, setIsPopperOpen] = useState(false);

const popperRef = useRef<HTMLDivElement>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(
null
);

const popper = usePopper(popperRef.current, popperElement, {
placement: 'bottom-start'
});

const closePopper = () => {
setIsPopperOpen(false);
buttonRef?.current?.focus();
};

const handleInputChange: ChangeEventHandler<HTMLInputElement> = (e) => {
setInputValue(e.currentTarget.value);
const date = parse(e.currentTarget.value, 'y-MM-dd', new Date());
if (isValid(date)) {
setSelected(date);
} else {
setSelected(undefined);
}
};

const handleButtonClick = () => {
setIsPopperOpen(true);
};

const handleDaySelect: SelectSingleEventHandler = (date) => {
setSelected(date);
if (date) {
setInputValue(format(date, 'y-MM-dd'));
closePopper();
} else {
setInputValue('');
}
};

return (
<div>
<div ref={popperRef}>
<input
size={12}
type="text"
placeholder={format(new Date(), 'y-MM-dd')}
value={inputValue}
onChange={handleInputChange}
/>
<button
ref={buttonRef}
type="button"
aria-label="Pick a date"
onClick={handleButtonClick}
>
Pick a date
</button>
</div>
{isPopperOpen && (
<FocusTrap
active
focusTrapOptions={{
initialFocus: false,
allowOutsideClick: true,
clickOutsideDeactivates: true,
onDeactivate: closePopper,
fallbackFocus: buttonRef.current || undefined
}}
>
<div
tabIndex={-1}
style={popper.styles.popper}
className="dialog-sheet"
{...popper.attributes.popper}
ref={setPopperElement}
role="dialog"
aria-label="DayPicker calendar"
>
<DayPicker
initialFocus={isPopperOpen}
mode="single"
defaultMonth={selected}
selected={selected}
onSelect={handleDaySelect}
/>
</div>
</FocusTrap>
)}
</div>
);
}

Example: Range Selection

|
import React, { ChangeEventHandler, useState } from 'react';

import { format, isAfter, isBefore, isValid, parse } from 'date-fns';
import {
DateRange,
DayPicker,
SelectRangeEventHandler
} from 'react-day-picker';

export default function App() {
const [selectedRange, setSelectedRange] = useState<DateRange>();
const [fromValue, setFromValue] = useState<string>('');
const [toValue, setToValue] = useState<string>('');

const handleFromChange: ChangeEventHandler<HTMLInputElement> = (e) => {
setFromValue(e.target.value);
const date = parse(e.target.value, 'y-MM-dd', new Date());
if (!isValid(date)) {
return setSelectedRange({ from: undefined, to: undefined });
}
if (selectedRange?.to && isAfter(date, selectedRange.to)) {
setSelectedRange({ from: selectedRange.to, to: date });
} else {
setSelectedRange({ from: date, to: selectedRange?.to });
}
};

const handleToChange: ChangeEventHandler<HTMLInputElement> = (e) => {
setToValue(e.target.value);
const date = parse(e.target.value, 'y-MM-dd', new Date());

if (!isValid(date)) {
return setSelectedRange({ from: selectedRange?.from, to: undefined });
}
if (selectedRange?.from && isBefore(date, selectedRange.from)) {
setSelectedRange({ from: date, to: selectedRange.from });
} else {
setSelectedRange({ from: selectedRange?.from, to: date });
}
};

const handleRangeSelect: SelectRangeEventHandler = (
range: DateRange | undefined
) => {
setSelectedRange(range);
if (range?.from) {
setFromValue(format(range.from, 'y-MM-dd'));
} else {
setFromValue('');
}
if (range?.to) {
setToValue(format(range.to, 'y-MM-dd'));
} else {
setToValue('');
}
};

return (
<DayPicker
mode="range"
selected={selectedRange}
onSelect={handleRangeSelect}
footer={
<form className="ma2">
<input
size={10}
placeholder="From Date"
value={fromValue}
onChange={handleFromChange}
/>
{' – '}
<input
size={10}
placeholder="To Date"
value={toValue}
onChange={handleToChange}
/>
</form>
}
/>
);
}
SuMoTuWeThFrSa

Example: Time Selection

DayPicker can also be used alongside a time input field, by setting the time to the selected date.

|
import React from 'react';

import { DayPicker } from 'react-day-picker';

export default function App() {
const [selected, setSelected] = React.useState<Date>();
const [timeValue, setTimeValue] = React.useState<string>('00:00');

const handleTimeChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
const time = e.target.value;
if (!selected) {
setTimeValue(time);
return;
}
const [hours, minutes] = time.split(':').map((str) => parseInt(str, 10));
const newSelectedDate = new Date(
selected.getFullYear(),
selected.getMonth(),
selected.getDate(),
hours,
minutes
);
setSelected(newSelectedDate);
setTimeValue(time);
};

const handleDaySelect = (date: Date | undefined) => {
if (!timeValue || !date) {
setSelected(date);
return;
}
const [hours, minutes] = timeValue
.split(':')
.map((str) => parseInt(str, 10));
const newDate = new Date(
date.getFullYear(),
date.getMonth(),
date.getDate(),
hours,
minutes
);
setSelected(newDate);
};

return (
<>
<DayPicker
mode="single"
selected={selected}
onSelect={handleDaySelect}
footer={
<>
<p>
Pick a time:{' '}
<input
type="time"
value={timeValue}
onChange={handleTimeChange}
/>
</p>
<p>
Selected date: {selected ? selected.toLocaleString() : 'none'}
</p>
</>
}
/>
</>
);
}
SuMoTuWeThFrSa

Pick a time:

Selected date: none

Using the useInput Hook

To bind DayPicker to an input field, DayPicker includes the useInput hook, returning props to bind DayPicker with a single input field. Should you need something more sophisticated, give a look to the useInput source to implement your own hook.

|
import React from 'react';

import { DayPicker, useInput } from 'react-day-picker';

export default function App() {
const { inputProps, dayPickerProps } = useInput({
defaultSelected: new Date(),
fromYear: 2021,
toYear: 2023,
format: 'PP',
required: true
});

const footer = (
<form>
<label>
<input {...inputProps} />
</label>
</form>
);
return <DayPicker {...dayPickerProps} footer={footer} />;
}
SuMoTuWeThFrSa