Date range picker
The Date range picker component allows users to select a range of dates via a calendar or text input.
import { ... } from '@ag.ds-next/react/date-range-picker';
() => { const [value, setValue] = React.useState({ from: undefined, to: undefined }); return ( <DateRangePicker value={value} onChange={setValue} onFromInputChange={(from) => setValue({ ...value, from })} onToInputChange={(to) => setValue({ ...value, to })} /> ); };
Date range picker is a controlled component which means consumers of this component need to manage the state of this component by using the value
, onChange
, onFromInputChange
and onToInputChange
props.
For an example of using this component in a form built with react-hook-form and yup, please see the Single-page form template.
Do
- always display the date format above the text input
- prefer Australian date format 'dd/mm/yyyy'
- allow users to navigate dates via the calendar
- ensure users can enter dates via the calendar or text input
- make sure error messages are specific about what input is required.
Don’t
- hide the date format
- use international date formats such as USA mm/dd/yyyy
- hijack built-in keyboard navigation behaviour.
Tracking the input value
The Date range picker component has two methods of input:
- Typing in a date string (dd/mm/yyyy) via the text inputs
- Selecting a date via the calendar widgets
Since we do not have any sort of input masking due to accessibility and user experience concerns, it is possible for the user to enter invalid values via the text inputs.
In this case, you can use the onFromInputChange
and onToInputChange
props to keep track of the user’s input. The value.from
and value.to
props can also be set to a string, which represents the value of the text inputs.
() => { // Set the value to a value that the user might think is valid const [value, setValue] = React.useState({ from: '30/1o/2020', to: '31/1o/2020', }); // This logic is for documentation purposes only. This should be done with `yup` or `zod`. // See Single-page form template const isInvalid = React.useCallback((value) => { if (typeof value === 'undefined' || value == '') return false; if (value instanceof Date && !isNaN(value.getTime())) return false; return true; }, []); const fromInvalid = isInvalid(value.from); const toInvalid = isInvalid(value.from); const onFromInputChange = (fromValue) => { console.log('onFromInputChange', fromValue); setValue({ ...value, fromValue }); }; const onToInputChange = (toValue) => { console.log('onToInputChange', toValue); setValue({ ...value, toValue }); }; const onChange = (dateRange) => { console.log('onChange', dateRange); setValue(dateRange); }; return ( <DateRangePicker value={value} onChange={onChange} onFromInputChange={onFromInputChange} onToInputChange={onToInputChange} fromInvalid={fromInvalid} toInvalid={toInvalid} message={fromInvalid || toInvalid ? 'Enter a valid date' : undefined} /> ); };
Legend
Use the legend
prop to describe the purpose of the group of fields.
() => { const [value, setValue] = React.useState({ from: undefined, to: undefined }); return ( <DateRangePicker value={value} onChange={setValue} onFromInputChange={(from) => setValue({ ...value, from })} onToInputChange={(to) => setValue({ ...value, to })} legend="Date period" /> ); };
Hint
Use the hint
prop to provide help that’s relevant to the majority of users, like how their information will be used, or where to find it.
Don’t use long paragraphs and lists in hint text. Screen readers read out the entire text when users interact with the form element. This could frustrate users if the text is long.
Don’t include links within hint text. While screen readers will read out the link text when describing the field, they will not tell users that the text is a link.
() => { const [value, setValue] = React.useState({ from: undefined, to: undefined }); return ( <DateRangePicker value={value} onChange={setValue} onFromInputChange={(from) => setValue({ ...value, from })} onToInputChange={(to) => setValue({ ...value, to })} legend="Date period" hint="Example hint text" /> ); };
Invalid
Use the fromInvalid
and toInvalid
props to indicate if the user input is invalid.
() => { const [value, setValue] = React.useState({ from: undefined, to: undefined }); return ( <FormStack> <DateRangePicker value={value} onChange={setValue} onFromInputChange={(from) => setValue({ ...value, from })} onToInputChange={(to) => setValue({ ...value, to })} fromInvalid={true} toInvalid={true} legend="Date period" message="Enter a valid date" /> <DateRangePicker value={value} onChange={setValue} onFromInputChange={(from) => setValue({ ...value, from })} onToInputChange={(to) => setValue({ ...value, to })} fromInvalid={true} toInvalid={false} legend="Date period" message="Enter a valid date" /> <DateRangePicker value={value} onChange={setValue} onFromInputChange={(from) => setValue({ ...value, from })} onToInputChange={(to) => setValue({ ...value, to })} fromInvalid={false} toInvalid={true} legend="Date period" message="Enter a valid date" /> </FormStack> ); };
Custom labels
Use the fromLabel
and toLabel
props to change the field labels.
() => { const [value, setValue] = React.useState({ from: undefined, to: undefined }); return ( <DateRangePicker value={value} onChange={setValue} onFromInputChange={(from) => setValue({ ...value, from })} onToInputChange={(to) => setValue({ ...value, to })} fromLabel="From" toLabel="To" /> ); };
Disabled
Disabled input elements are unusable and can not be clicked. This prevents a user from interacting with the input element until another action is complete.
() => { const [value, setValue] = React.useState({ from: undefined, to: undefined }); return ( <DateRangePicker value={value} onChange={setValue} onFromInputChange={(from) => setValue({ ...value, from })} onToInputChange={(to) => setValue({ ...value, to })} disabled /> ); };
Minimum and maximum dates
The minDate
property can be used to disable any days before a specific date.
The maxDate
property can be used to disable any days after a specific date.
If a valid date is entered using the text input but it falls outside the constrained range, the closest valid date will be used.
() => { const [value, setValue] = React.useState({ from: undefined, to: undefined }); const today = new Date(); const lastWeek = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000); const nextWeek = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000); return ( <DateRangePicker value={value} onChange={setValue} onFromInputChange={(from) => setValue({ ...value, from })} onToInputChange={(to) => setValue({ ...value, to })} minDate={lastWeek} maxDate={nextWeek} /> ); };
Changing the date format
The Date range picker component allows users to enter dates in various formats. To select the 18th February 2023, a user could input '18/02/2023', '18 Feb 2023', '18th February 2023', or any other supported date format.
When a valid date is typed, the date will be formatted using the dateFormat
prop, which is set to 'dd/MM/yyyy'
by default.
To modify the date format, you can change the dateFormat
prop to one of these supported date formats:
'dd/MM/yyyy'
(e.g. 18/02/2023)'dd-MM-yyyy'
(e.g. 18-02-2023)'dd MM yyyy'
(e.g. 18 02 2023)'MM/dd/yyyy'
(e.g. 02/18/2023)'MM-dd-yyyy'
(e.g. 02-18-2023)'MM dd yyyy'
(e.g. 02 18 2023)'do MMMM yyyy'
(e.g. 8th February 2023)'do MMM yyyy'
(e.g. 8th Feb 2023)'MMMM do yyyy'
(e.g. February 8th 2023)'MMM do yyyy'
(e.g. Feb 8th 2023)'d MMMM yyyy'
(e.g. 8 February 2023)'d MMM yyyy'
(e.g. 8 Feb 2023)'MMMM d yyyy'
(e.g. February 8 2023)'MMM d yyyy'
(e.g. Feb 8 2023)'dd MMMM yyyy'
(e.g. 08 February 2023)'dd MMM yyyy'
(e.g. 08 Feb 2023)'MMMM dd yyyy'
(e.g. February 08 2023)'MMM dd yyyy'
(e.g. Feb 08 2023)
() => { const [value, setValue] = React.useState({ from: undefined, to: undefined }); return ( <DateRangePicker value={value} onChange={setValue} onFromInputChange={(from) => setValue({ ...value, from })} onToInputChange={(to) => setValue({ ...value, to })} dateFormat="d MMM yyyy" /> ); };
Changing the allowed date formats
By default, all of the supported date formats are allowed, but should you need to restrict the formats that can be parsed and validated, you can pass in an array of date format strings to the allowedDateFormats
prop.
Some applications may find it useful to remove US formats, since an invalid AU date could become a valid US date and cause confusion for users.
Date formats are parsed in order from first to last and stops when a valid date is found. The preferred dateFormat
always becomes the first to check, even if omitted in allowedDateFormats
.
() => { // Set the value to a value that the user might think is valid const [value, setValue] = React.useState({ from: '', to: '' }); // This logic is for documentation purposes only. This should be done with `yup` or `zod`. // See Single-page form template const isInvalid = React.useCallback((value) => { if (typeof value === 'undefined' || value == '') return false; if (value instanceof Date && !isNaN(value.getTime())) return false; return true; }, []); const fromInvalid = isInvalid(value.from); const toInvalid = isInvalid(value.from); const onFromInputChange = (fromValue) => { console.log('onFromInputChange', fromValue); setValue({ ...value, fromValue }); }; const onToInputChange = (toValue) => { console.log('onToInputChange', toValue); setValue({ ...value, toValue }); }; const onChange = (dateRange) => { console.log('onChange', dateRange); setValue(dateRange); }; return ( <DateRangePicker value={value} onChange={onChange} onFromInputChange={onFromInputChange} onToInputChange={onToInputChange} fromInvalid={fromInvalid} toInvalid={toInvalid} message={fromInvalid || toInvalid ? 'Enter a valid date' : undefined} hint="Only short AU formats allowed" allowedDateFormats={['dd MM yyyy', 'dd-MM-yyyy']} /> ); };
Related components
- Date picker – The Date picker component allows users to select a single date via a calendar or text input.