---
title: "React Popup Range picker with presets Example | Mobiscroll"
component: "Popup"
framework: "React"
library_version: "6.2.0"
url: "https://demo.mobiscroll.com/react/popup/range-picker-popup-presets"
---

# Range picker with presets - React Popup demo

Use the React popup to create advanced range picker components with presets and more. For React and Next.JS.

- [Live demo](https://demo.mobiscroll.com/react/popup/range-picker-popup-presets)
- [View React JS code on GitHub](https://github.com/acidb/mobiscroll-demos-react/tree/main/src/demos/pickers/popup/range-picker-popup-presets)
- [View React TS code on GitHub](https://github.com/acidb/mobiscroll-demos-react-ts/tree/main/src/demos/pickers/popup/range-picker-popup-presets)
- This demo in other frameworks: [JavaScript](https://demo.mobiscroll.com/javascript/popup/range-picker-popup-presets.md), [jQuery](https://demo.mobiscroll.com/jquery/popup/range-picker-popup-presets.md), [Angular](https://demo.mobiscroll.com/angular/popup/range-picker-popup-presets.md), [Vue](https://demo.mobiscroll.com/vue/popup/range-picker-popup-presets.md)

## Description

Build complex pickers to show within popups to make things easy for your users. A great example is date-range selection with presets. If you've ever used Google Analytics, you have probably seen the date range selector for filtering the data, which is very similar to what you can see in this example. Select custom start and end dates along with presets is helpful for easy access to predefined ranges that people are usually interested in.

**Would like to explore more date & time picker functionality?** [Check out the range examples we have for you →](https://demo.mobiscroll.com/react/range)

## What this demo shows

- Shows a clickable input which opens a custom range picker with preset options.
- **Popup behavior** The range picker opens below the input and closes when the user clicks outside it.
- **Month view** The popup contains an inline date picker for selecting start and end dates. A fixed weekday header displays abbreviated day names from Sunday through Saturday, with the dates arranged in a grid below.
- **Header navigation** Clicking the month and year label in the upper-left corner opens the month and year picker. The previous and next arrow buttons on the right move between months.
- **Swipe navigation** Dragging the calendar left or right moves between months.
- **Day cell states** Hovering over a day displays a gray background behind its number. The first selected day becomes the range start, and the second becomes the range end. Selected dates appear in blue. The current date is highlighted by default and remains blue when another date is selected.
- **Adjacent months** Dates from the previous and next months use a muted style. Selecting one opens its corresponding month.
- **Presets** The **Date range** dropdown beside the inline calendar provides **Custom**, **Today**, **Yesterday**, **Last week**, **Last month**, **Last 7 days**, and **Last 30 days** options. **Custom** is selected by default and allows free start and end date selection from the calendar.
- **Start and end inputs** The **Start** and **End** inputs below the preset dropdown support date selection and manual date entry. They are enabled only when **Custom** is selected.
- **Actions** The `Apply` button confirms the selected date range, while the gray `Cancel` button discards the change.
- **Input value** After the user confirms the selection with `Apply`, the input displays the date range in month, day, and year format.

## Best for

- **Analytics and reporting** Filter dashboards and reports by common periods such as today, last week, or last month, while retaining a custom range option.
- **Transaction history** Review orders, payments, or account activity across frequently used date ranges.
- **Logs and audit records** Narrow time-based records to recent periods or a manually entered start and end date.
- **Operational dashboards** Switch quickly between standard monitoring windows such as the last 7 or 30 days.
- **Search and data tables** Add compact date filtering where users need both preset ranges and precise custom selection.

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/react/popup/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/react/popup/api#opt-themeVariant) - option
- [`touchUi`](https://mobiscroll.com/docs/react/select/api#opt-touchUi) - option
- [`display`](https://mobiscroll.com/docs/react/popup/api#opt-display) - option
- [`touchUi`](https://mobiscroll.com/docs/react/popup/api#opt-touchUi) - option
- [`buttons`](https://mobiscroll.com/docs/react/popup/api#opt-buttons) - option
- [`anchor`](https://mobiscroll.com/docs/react/popup/api#opt-anchor) - option
- [`scrollLock`](https://mobiscroll.com/docs/react/popup/api#opt-scrollLock) - option
- [`showArrow`](https://mobiscroll.com/docs/react/popup/api#opt-showArrow) - option
- [`maxWidth`](https://mobiscroll.com/docs/react/popup/api#opt-maxWidth) - option
- [`isOpen`](https://mobiscroll.com/docs/react/popup/api#opt-isOpen) - option
- [`onClose`](https://mobiscroll.com/docs/react/popup/api#event-onClose) - event
- [`responsive`](https://mobiscroll.com/docs/react/popup/api#opt-responsive) - option
- [`cssClass`](https://mobiscroll.com/docs/react/popup/api#opt-cssClass) - option
- [`data`](https://mobiscroll.com/docs/react/select/api#opt-data) - option
- [`label`](https://mobiscroll.com/docs/react/select/api#opt-label) - option
- [`labelStyle`](https://mobiscroll.com/docs/react/select/api#opt-labelStyle) - option
- [`inputStyle`](https://mobiscroll.com/docs/react/select/api#opt-inputStyle) - option
- [`responsive`](https://mobiscroll.com/docs/react/select/api#opt-responsive) - option
- [`onChange`](https://mobiscroll.com/docs/react/select/api#event-onChange) - event
- [`disabled`](https://mobiscroll.com/docs/react/forms/input#opt-disabled) - option
- [`select`](https://mobiscroll.com/docs/react/datepicker/api#opt-select) - option
- [`display`](https://mobiscroll.com/docs/react/datepicker/api#opt-display) - option
- [`showRangeLabels`](https://mobiscroll.com/docs/react/datepicker/api#opt-showRangeLabels) - option
- [`pages`](https://mobiscroll.com/docs/react/datepicker/api#opt-pages) - option
- [`startInput`](https://mobiscroll.com/docs/react/datepicker/api#opt-startInput) - option
- [`endInput`](https://mobiscroll.com/docs/react/datepicker/api#opt-endInput) - option
- [`returnFormat`](https://mobiscroll.com/docs/react/datepicker/api#opt-returnFormat) - option
- [`showOnClick`](https://mobiscroll.com/docs/react/datepicker/api#opt-showOnClick) - option
- [`showOnFocus`](https://mobiscroll.com/docs/react/datepicker/api#opt-showOnFocus) - option
- [`onChange`](https://mobiscroll.com/docs/react/datepicker/api#event-onChange) - event

## Code

### JSX

```jsx
import { Button, Datepicker, formatDate, Input, options, Page, Popup, Select, setOptions } from '@mobiscroll/react';
import { useCallback, useEffect, useState } from 'react';

setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

const startDate = '2026-11-04T00:00';
const endDate = '2026-11-10T00: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-04T00:00', '2026-11-04T00:00']);
          break;
        case 'yesterday':
          setSelectedDate(['2026-11-03T00:00', '2026-11-03T00: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-29T00:00', '2026-11-04T00:00']);
          break;
        case 'last-30-days':
          setSelectedDate(['2026-10-06T00:00', '2026-11-04T00:00']);
          break;
        default:
          break;
      }
    }
    setSelected(s);
  }, []);

  const onDateChange = useCallback((ev) => {
    const date = ev.value;

    setDisabledInput(false);
    setSelected('custom');
    setSelectedDate(date);
  }, []);

  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;
```

### TSX

```tsx
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({
  theme: 'ios',
  themeVariant: 'light'
});

const startDate = '2026-11-04T00:00';
const endDate = '2026-11-10T00: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-04T00:00', '2026-11-04T00:00']);
          break;
        case 'yesterday':
          setSelectedDate(['2026-11-03T00:00', '2026-11-03T00: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-29T00:00', '2026-11-04T00:00']);
          break;
        case 'last-30-days':
          setSelectedDate(['2026-10-06T00:00', '2026-11-04T00:00']);
          break;
        default:
          break;
      }
    }
    setSelected(s);
  }, []);

  const onDateChange = useCallback((ev: MbscDatepickerChangeEvent) => {
    const date = ev.value as MbscDateType[];

    setDisabledInput(false);
    setSelected('custom');
    setSelectedDate(date);
  }, []);

  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;
```

### CSS

```css
.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;
  }
}
```

## Implementation notes

- `Popup` is opened by clicking the filter `Input` (`isOpen`/`:isOpen` bound to state set `true` on click in React/Vue; `popup.open()` via a `@ViewChild`/template-ref instance in Angular; `.setOptions`/`.open()` on the instance in JS/jQuery) and closed via `onClose`/`@close`.
- `responsive` provides breakpoint-specific popup configs: an `xsmall` entry (`display: 'bottom'`, `touchUi: true`, a custom `'Apply'` button object plus `'cancel'`) for small screens, and a `custom` entry (`breakpoint: 559`, `buttons: []`, `display: 'anchored'`, `anchor` set to the filter input, `anchorAlign: 'start'`, `touchUi: false`, `scrollLock: false`, `showArrow: false`, `maxWidth: 920`) for larger screens - so the same popup renders as a bottom sheet with its own Apply/Cancel buttons on mobile and as an anchored, arrow-less panel with page-level Apply/Cancel buttons rendered outside the popup on desktop.
- The `Select` (`Date range`) uses `data` entries with `value`/`text` pairs (`custom`, `today`, `yesterday`, `last-week`, `last-month`, `last-7-days`, `last-30-days`); its own `responsive` option switches its `touchUi` between breakpoints independently of the popup's.
- Choosing anything other than `'custom'` in `onChange`/`@change` computes a `{ start, end }` pair (e.g. `today` → today/today, `last-7-days` → 6 days ago/today) and disables the Start/End `Input`s (`disabled`/`[disabled]`/`:disabled`); choosing `'custom'` re-enables them.
- The nested `Datepicker` uses `select: 'range'`, `display: 'inline'`, `showRangeLabels: false`, `pages: 'auto'`, `returnFormat: 'iso8601'`, `showOnClick: false`, and `showOnFocus: false` - it renders inline inside the popup rather than opening its own popup, always returns ISO 8601 date strings/pairs, and doesn't open on clicking or focusing its bound inputs. It's wired to the Start/End `Input`s via `startInput`/`endInput` (`[startInput]`/`[endInput]` in Angular, `:startInput`/`:endInput` in Vue, refs in React, element IDs in JS/jQuery).
- Picking a date in the calendar's `onChange`/`@change` sets `selected` back to `'custom'` and re-enables the Start/End inputs, overriding whatever preset was active.
- `Apply` reformats the selected `{ start, end }` pair through `formatDate()` (using `options.locale.dateFormat`, defaulting to `'DD/MM/YYYY'`) into the filter input's display value and closes the popup; `Cancel` closes the popup without changing the input value.

## Run it in your project

1. If not signed up yet, or don't own a license, you can [start a free trial](https://mobiscroll.com/starttrial).

2. Clone the repository:

   ```
   git clone https://github.com/acidb/mobiscroll-demos-react.git
   ```

3. If not installed yet, install the Mobiscroll CLI from NPM:

   ```
   npm install -g @mobiscroll/cli
   ```

4. Config Mobiscroll - open a terminal window in the root directory and run:

   ```
   mobiscroll config react
   ```

5. Install dependencies - open a terminal window in the root directory and run:

   ```
   npm install
   ```

6. Run the project:

   ```
   npm run dev
   ```

On a trial, add `--trial` to the config command: `mobiscroll config react --trial`.

Running a demo locally requires a Mobiscroll trial or a license; setup is per framework at https://mobiscroll.com/docs/react/getting-started/installation

## Related demos

- Previous: [Using as a tooltip](https://demo.mobiscroll.com/react/popup/show-hide-tooltip-hover-in-out.md) - Use the React popup with custom content on event hover within the various event calendar views.
