React Range Date Filter with Preset Ranges
Get started with this exampleUsage
theme="ios" You can turn auto theming on and let it decide based on the platform.
Usage
themeVariant="light" You can turn auto dark mode on based on system settings.
Usage
touchUi={true}
// touch interaction is enabled Use touch interaction on mobile or pointer interaction on desktop.
Usage
locale={localeEn} Selecting the locale loads the localized settings and the translated UI text.
Date Filter with Preset Ranges
When building a date filtering UI a date range picker is essential. On top of being able to select custom start and end dates presets can be very helpful and provide a nice productivity boost. A more advanced filtering experience can be created in a couple of steps:
- Custom popup with an inline calendar - Create a custom popup and place an inline date picker with start/end selection
- Select with common presets - Populate a dropdown with values like Today, Yesterday, Last week, Last month, Last 7 days, Last 30 days
- Inputs for typing dates - Adding two inputs next to the range picker enables users to type in a date rather than solely relying on manual selection
- Make it fully responsive - Use the
responsiveapi to customize the desktop experience - dropdown with inputs for typing - and the mobile experience - touch optimized interface
Demo
Code
View code on GitHub
import { Button, Datepicker, formatDate, Input, options, Page, Popup, Select, setOptions } from '@mobiscroll/react';
import { useCallback, useEffect, useState } from 'react';
setOptions({
locale: ,
theme: ' ',
themeVariant: ' '
});
const startDate = '2026-11-06T00:00';
const endDate = '2026-11-12T00:00';
const now = new Date();
const day = now.getDay();
const monday = now.getDate() - day + (day === 0 ? -6 : 1);
const respSelect = {
xsmall: { touchUi: true },
small: { touchUi: false },
};
const myData = [
{ value: 'custom', text: 'Custom' },
{ value: 'today', text: 'Today' },
{ value: 'yesterday', text: 'Yesterday' },
{ value: 'last-week', text: 'Last week' },
{ value: 'last-month', text: 'Last month' },
{ value: 'last-7-days', text: 'Last 7 days' },
{ value: 'last-30-days', text: 'Last 30 days' },
];
function App() {
const [isOpen, setOpen] = useState(false);
const [start, startRef] = useState(null);
const [end, endRef] = useState(null);
const [selected, setSelected] = useState('custom');
const [selectedDate, setSelectedDate] = useState([startDate, endDate]);
const [inputValue, setInputValue] = useState('');
const [disabledInput, setDisabledInput] = useState(false);
const respPopup = {
xsmall: {
display: 'bottom',
touchUi: true,
buttons: [
{
text: 'Apply',
handler: () => {
const date = selectedDate;
changeInputValue(date[0], date[1] || date[0]);
setOpen(false);
},
},
'cancel',
],
},
custom: {
breakpoint: 559,
buttons: [],
display: 'anchored',
anchor: document.querySelector('.date-filter-input'),
anchorAlign: 'start',
touchUi: false,
scrollLock: false,
showArrow: false,
maxWidth: 920,
},
};
const inputClick = useCallback(() => {
setOpen(true);
}, []);
const changeInputValue = useCallback((start, end) => {
const locale = options.locale || {};
const dateFormat = locale.dateFormat || 'DD/MM/YYYY';
setInputValue(formatDate(dateFormat, new Date(start)) + ' - ' + formatDate(dateFormat, new Date(end)));
}, []);
const applyClick = useCallback(() => {
changeInputValue(selectedDate[0], selectedDate[1] || selectedDate[0]);
setOpen(false);
}, [selectedDate, changeInputValue]);
const cancelClick = useCallback(() => {
setOpen(false);
}, []);
const onChange = useCallback((event) => {
const s = event.value;
if (s === 'custom') {
setDisabledInput(false);
} else {
setDisabledInput(true);
switch (s) {
case 'today':
setSelectedDate(['2026-11-06T00:00', '2026-11-06T00:00']);
break;
case 'yesterday':
setSelectedDate(['2026-11-05T00:00', '2026-11-05T00:00']);
break;
case 'last-week':
setSelectedDate([
new Date(now.getFullYear(), now.getMonth(), monday - 7),
new Date(now.getFullYear(), now.getMonth(), monday - 1),
]);
break;
case 'last-month':
setSelectedDate(['2026-10-01T00:00', '2026-10-31T00:00']);
break;
case 'last-7-days':
setSelectedDate(['2026-10-31T00:00', '2026-11-06T00:00']);
break;
case 'last-30-days':
setSelectedDate(['2026-10-08T00:00', '2026-11-06T00:00']);
break;
default:
break;
}
}
setSelected(s);
}, []);
const onDateChange = useCallback((ev) => {
setDisabledInput(false);
setSelected('custom');
setSelectedDate(ev.value);
}, []);
const onClose = useCallback(() => {
setOpen(false);
}, []);
useEffect(() => {
changeInputValue(startDate, endDate);
});
return (
<Page>
<div className="mbsc-form-group">
<Input inputStyle="box" onClick={inputClick} defaultValue={inputValue} className="date-filter-input"></Input>
</div>
<Popup isOpen={isOpen} onClose={onClose} responsive={respPopup} cssClass="demo-date-filtering-popup">
<div className="mbsc-grid mbsc-no-padding">
<div className="mbsc-row">
<div className="mbsc-col-sm-4 mbsc-push-sm-8 demo-date-filtering-dates">
<div className="demo-date-filtering-inputs">
<Select
data={myData}
label="Date range"
labelStyle="stacked"
inputStyle="box"
responsive={respSelect}
touchUi={true}
value={selected}
onChange={onChange}
/>
<Input
ref={startRef}
disabled={disabledInput}
label="Start"
labelStyle="stacked"
inputStyle="box"
className="demo-date-filtering-range-input"
placeholder="Please Select..."
/>
<Input
ref={endRef}
disabled={disabledInput}
label="End"
labelStyle="stacked"
inputStyle="box"
className="demo-date-filtering-range-input"
placeholder="Please Select..."
/>
</div>
<div className="demo-date-filtering-desktop-buttons mbsc-button-group-justified">
<Button className="apply-button" onClick={applyClick}>
Apply
</Button>
<Button className="cancel-button" onClick={cancelClick}>
Cancel
</Button>
</div>
</div>
<div className="mbsc-col-sm-8 mbsc-pull-sm-4">
<Datepicker
select="range"
display="inline"
showRangeLabels={false}
pages="auto"
startInput={start}
endInput={end}
returnFormat="iso8601"
showOnClick={false}
showOnFocus={false}
value={selectedDate}
onChange={onDateChange}
/>
</div>
</div>
</div>
</Popup>
</Page>
);
}
export default App;
import {
Button,
Datepicker,
formatDate,
Input,
MbscDatepickerChangeEvent,
MbscDateType,
MbscPopupOptions,
MbscResponsiveOptions,
MbscSelectChangeEvent,
options,
Page,
Popup,
Select,
setOptions ,
} from '@mobiscroll/react';
import { FC, useCallback, useEffect, useState } from 'react';
setOptions({
locale: ,
theme: ' ',
themeVariant: ' '
});
const startDate = '2026-11-06T00:00';
const endDate = '2026-11-12T00:00';
const now = new Date();
const day = now.getDay();
const monday = now.getDate() - day + (day === 0 ? -6 : 1);
const respSelect = {
xsmall: { touchUi: true },
small: { touchUi: false },
};
const myData = [
{ value: 'custom', text: 'Custom' },
{ value: 'today', text: 'Today' },
{ value: 'yesterday', text: 'Yesterday' },
{ value: 'last-week', text: 'Last week' },
{ value: 'last-month', text: 'Last month' },
{ value: 'last-7-days', text: 'Last 7 days' },
{ value: 'last-30-days', text: 'Last 30 days' },
];
const App: FC = () => {
const [isOpen, setOpen] = useState<boolean>(false);
const [start, startRef] = useState<Input | null>(null);
const [end, endRef] = useState<Input | null>(null);
const [selected, setSelected] = useState<string>('custom');
const [selectedDate, setSelectedDate] = useState<MbscDateType[]>([startDate, endDate]);
const [inputValue, setInputValue] = useState<string>('');
const [disabledInput, setDisabledInput] = useState<boolean>(false);
const respPopup: MbscResponsiveOptions<MbscPopupOptions> = {
xsmall: {
display: 'bottom',
touchUi: true,
buttons: [
{
text: 'Apply',
handler: () => {
const date = selectedDate;
changeInputValue(date[0] as string, (date[1] as string) || (date[0] as string));
setOpen(false);
},
},
'cancel',
],
},
custom: {
breakpoint: 559,
buttons: [],
display: 'anchored',
anchor: document.querySelector('.date-filter-input') as HTMLInputElement,
anchorAlign: 'start',
touchUi: false,
scrollLock: false,
showArrow: false,
maxWidth: 920,
},
};
const inputClick = useCallback(() => {
setOpen(true);
}, []);
const changeInputValue = useCallback((start: string, end: string) => {
const locale = options.locale || {};
const dateFormat = locale.dateFormat || 'DD/MM/YYYY';
setInputValue(formatDate(dateFormat, new Date(start)) + ' - ' + formatDate(dateFormat, new Date(end)));
}, []);
const applyClick = useCallback(() => {
changeInputValue(selectedDate[0] as string, (selectedDate[1] as string) || (selectedDate[0] as string));
setOpen(false);
}, [selectedDate, changeInputValue]);
const cancelClick = useCallback(() => {
setOpen(false);
}, []);
const onChange = useCallback((event: MbscSelectChangeEvent) => {
const s = event.value;
if (s === 'custom') {
setDisabledInput(false);
} else {
setDisabledInput(true);
switch (s) {
case 'today':
setSelectedDate(['2026-11-06T00:00', '2026-11-06T00:00']);
break;
case 'yesterday':
setSelectedDate(['2026-11-05T00:00', '2026-11-05T00:00']);
break;
case 'last-week':
setSelectedDate([
new Date(now.getFullYear(), now.getMonth(), monday - 7),
new Date(now.getFullYear(), now.getMonth(), monday - 1),
]);
break;
case 'last-month':
setSelectedDate(['2026-10-01T00:00', '2026-10-31T00:00']);
break;
case 'last-7-days':
setSelectedDate(['2026-10-31T00:00', '2026-11-06T00:00']);
break;
case 'last-30-days':
setSelectedDate(['2026-10-08T00:00', '2026-11-06T00:00']);
break;
default:
break;
}
}
setSelected(s);
}, []);
const onDateChange = useCallback((ev: MbscDatepickerChangeEvent) => {
setDisabledInput(false);
setSelected('custom');
setSelectedDate(ev.value as MbscDateType[]);
}, []);
const onClose = useCallback(() => {
setOpen(false);
}, []);
useEffect(() => {
changeInputValue(startDate, endDate);
});
return (
<Page>
<div className="mbsc-form-group">
<Input inputStyle="box" onClick={inputClick} defaultValue={inputValue} className="date-filter-input"></Input>
</div>
<Popup isOpen={isOpen} onClose={onClose} responsive={respPopup} cssClass="demo-date-filtering-popup">
<div className="mbsc-grid mbsc-no-padding">
<div className="mbsc-row">
<div className="mbsc-col-sm-4 mbsc-push-sm-8 demo-date-filtering-dates">
<div className="demo-date-filtering-inputs">
<Select
data={myData}
label="Date range"
labelStyle="stacked"
inputStyle="box"
responsive={respSelect}
touchUi={true}
value={selected}
onChange={onChange}
/>
<Input
ref={startRef}
disabled={disabledInput}
label="Start"
labelStyle="stacked"
inputStyle="box"
className="demo-date-filtering-range-input"
placeholder="Please Select..."
/>
<Input
ref={endRef}
disabled={disabledInput}
label="End"
labelStyle="stacked"
inputStyle="box"
className="demo-date-filtering-range-input"
placeholder="Please Select..."
/>
</div>
<div className="demo-date-filtering-desktop-buttons mbsc-button-group-justified">
<Button className="apply-button" onClick={applyClick}>
Apply
</Button>
<Button className="cancel-button" onClick={cancelClick}>
Cancel
</Button>
</div>
</div>
<div className="mbsc-col-sm-8 mbsc-pull-sm-4">
<Datepicker
select="range"
display="inline"
showRangeLabels={false}
pages="auto"
startInput={start}
endInput={end}
returnFormat="iso8601"
showOnClick={false}
showOnFocus={false}
value={selectedDate}
onChange={onDateChange}
/>
</div>
</div>
</div>
</Popup>
</Page>
);
};
export default App;
.demo-date-filtering-popup .mbsc-ios.mbsc-datepicker-inline {
border: none;
border-radius: 4px;
overflow: hidden;
}
.demo-date-filtering-popup .mbsc-textfield-wrapper-box {
margin-top: 0;
margin-right: 0;
}
.demo-date-filtering-dates {
display: flex;
flex-direction: column;
}
.demo-date-filtering-inputs {
flex: 1;
}
.demo-date-filtering-desktop-buttons.mbsc-button-group-justified,
.demo-date-filtering-desktop-buttons.mbsc-button-group-justified button {
margin-bottom: 0;
}
@media (max-width: 575px) {
.demo-date-filtering-desktop-buttons {
display: none;
}
.demo-date-filtering-popup .mbsc-textfield-wrapper-box {
margin-left: 0;
}
}