---
title: "Vue Range Date Filter with Preset Ranges Example | Mobiscroll"
component: "Range"
framework: "Vue"
library_version: "6.2.0"
url: "https://demo.mobiscroll.com/vue/range/date-filtering-with-predefined-ranges"
---

# Date Filter with Preset Ranges - Vue Range demo

Build a date filtering UI with the Vue date range picker — choose from custom or preset ranges (Today, Last 7 days, Last 30 days). For Vue and Nuxt.

- [Live demo](https://demo.mobiscroll.com/vue/range/date-filtering-with-predefined-ranges)
- [View SFC JS code on GitHub](https://github.com/acidb/mobiscroll-demos-vue/tree/main/src/demos/datepicker/range/date-filtering-with-predefined-ranges)
- [View SFC TS code on GitHub](https://github.com/acidb/mobiscroll-demos-vue-ts/tree/main/src/demos/datepicker/range/date-filtering-with-predefined-ranges)
- This demo in other frameworks: [JavaScript](https://demo.mobiscroll.com/javascript/range/date-filtering-with-predefined-ranges.md), [jQuery](https://demo.mobiscroll.com/jquery/range/date-filtering-with-predefined-ranges.md), [Angular](https://demo.mobiscroll.com/angular/range/date-filtering-with-predefined-ranges.md), [React](https://demo.mobiscroll.com/react/range/date-filtering-with-predefined-ranges.md)

## Description

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 `responsive` api to customize the desktop experience - dropdown with inputs for typing - and the mobile experience - touch optimized interface

## 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/vue/datepicker/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/vue/datepicker/api#opt-themeVariant) - option
- [`responsive`](https://mobiscroll.com/docs/vue/popup/api#opt-responsive) - option
- [`display`](https://mobiscroll.com/docs/vue/popup/api#opt-display) - option
- [`touchUi`](https://mobiscroll.com/docs/vue/popup/api#opt-touchUi) - option
- [`buttons`](https://mobiscroll.com/docs/vue/popup/api#opt-buttons) - option
- [`anchor`](https://mobiscroll.com/docs/vue/popup/api#opt-anchor) - option
- [`scrollLock`](https://mobiscroll.com/docs/vue/popup/api#opt-scrollLock) - option
- [`showArrow`](https://mobiscroll.com/docs/vue/popup/api#opt-showArrow) - option
- [`maxWidth`](https://mobiscroll.com/docs/vue/popup/api#opt-maxWidth) - option
- [`touchUi`](https://mobiscroll.com/docs/vue/select/api#opt-touchUi) - option
- [`inputStyle`](https://mobiscroll.com/docs/vue/forms/input#opt-inputStyle) - option
- [`labelStyle`](https://mobiscroll.com/docs/vue/forms/input#opt-labelStyle) - option
- [`isOpen`](https://mobiscroll.com/docs/vue/popup/api#opt-isOpen) - option
- [`close`](https://mobiscroll.com/docs/vue/popup/api#event-onClose) - event
- [`data`](https://mobiscroll.com/docs/vue/select/api#opt-data) - option
- [`responsive`](https://mobiscroll.com/docs/vue/select/api#opt-responsive) - option
- [`label`](https://mobiscroll.com/docs/vue/select/api#opt-label) - option
- [`inputStyle`](https://mobiscroll.com/docs/vue/select/api#opt-inputStyle) - option
- [`labelStyle`](https://mobiscroll.com/docs/vue/select/api#opt-labelStyle) - option
- [`change`](https://mobiscroll.com/docs/vue/select/api#event-onChange) - event
- [`disabled`](https://mobiscroll.com/docs/vue/forms/input#opt-disabled) - option
- [`select`](https://mobiscroll.com/docs/vue/datepicker/api#opt-select) - option
- [`display`](https://mobiscroll.com/docs/vue/datepicker/api#opt-display) - option
- [`pages`](https://mobiscroll.com/docs/vue/datepicker/api#opt-pages) - option
- [`returnFormat`](https://mobiscroll.com/docs/vue/datepicker/api#opt-returnFormat) - option
- [`startInput`](https://mobiscroll.com/docs/vue/datepicker/api#opt-startInput) - option
- [`endInput`](https://mobiscroll.com/docs/vue/datepicker/api#opt-endInput) - option
- [`controls`](https://mobiscroll.com/docs/vue/datepicker/api#opt-controls) - option
- [`showRangeLabels`](https://mobiscroll.com/docs/vue/datepicker/api#opt-showRangeLabels) - option
- [`showOnClick`](https://mobiscroll.com/docs/vue/datepicker/api#opt-showOnClick) - option
- [`showOnFocus`](https://mobiscroll.com/docs/vue/datepicker/api#opt-showOnFocus) - option
- [`change`](https://mobiscroll.com/docs/vue/datepicker/api#event-onChange) - event

## Code

### SFC JS

```vue
<script setup>
import {
  formatDate,
  MbscButton,
  MbscDatepicker,
  MbscInput,
  MbscPage,
  MbscPopup,
  MbscSelect,
  options,
  setOptions
} from '@mobiscroll/vue'
import { onMounted, ref } from 'vue'

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

const now = new Date()
const day = now.getDay()
const monday = now.getDate() - day + (day === 0 ? -6 : 1)
const startDate = '2026-11-01T00:00'
const endDate = '2026-11-07T00:00'

const startInput = ref(null)
const endInput = ref(null)
const dateInput = ref('')
const selected = ref('custom')
const selectedDate = ref([startDate, endDate])
const disableInput = ref(false)
const isPopupOpen = ref(false)

const respPopup = {
  responsive: {
    xsmall: {
      display: 'bottom',
      touchUi: true,
      buttons: [
        {
          text: 'Apply',
          handler: () => {
            const date = selectedDate.value

            changeInputValue(date[0], date[1] || date[0])
            isPopupOpen.value = true
          }
        },
        'cancel'
      ]
    },
    custom: {
      breakpoint: 559,
      buttons: [],
      display: 'anchored',
      anchor: document.querySelector('.date-filter-input'),
      anchorAlign: 'start',
      touchUi: false,
      scrollLock: false,
      showArrow: false,
      maxWidth: 920
    }
  }
}

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 handleSelectChange(event) {
  const s = event.value

  if (s === 'custom') {
    disableInput.value = false
  } else {
    disableInput.value = true

    switch (s) {
      case 'today':
        selectedDate.value = ['2021-09-15T00:00', '2021-09-15T00:00']
        break
      case 'yesterday':
        selectedDate.value = ['2021-09-14T00:00', '2021-09-14T00:00']
        break
      case 'last-week':
        selectedDate.value = [
          new Date(now.getFullYear(), now.getMonth(), monday - 7),
          new Date(now.getFullYear(), now.getMonth(), monday - 1)
        ]
        break
      case 'last-month':
        selectedDate.value = ['2021-08-01T00:00', '2021-08-31T00:00']
        break
      case 'last-7-days':
        selectedDate.value = ['2021-09-09T00:00', '2021-09-15T00:00']
        break
      case 'last-30-days':
        selectedDate.value = ['2021-08-17T00:00', '2021-09-15T00:00']
        break
      default:
        break
    }
  }
  selected.value = s
}

function handleCalendarChange() {
  disableInput.value = false
}

function handleInputClick() {
  isPopupOpen.value = true
}

function changeInputValue(start, end) {
  const locale = options.locale || {}
  const dateFormat = locale.dateFormat || 'DD/MM/YYYY'

  dateInput.value =
    formatDate(dateFormat, new Date(start)) + ' - ' + formatDate(dateFormat, new Date(end))
}

function applyClick() {
  const date = selectedDate.value

  changeInputValue(date[0], date[1] || date[0])
  isPopupOpen.value = false
}

function cancelClick() {
  isPopupOpen.value = false
}

function handlePopupClose() {
  isPopupOpen.value = false
}

onMounted(() => {
  changeInputValue(startDate, endDate)
})
</script>

<template>
  <MbscPage>
    <div class="mbsc-form-group">
      <MbscInput
        v-model="dateInput"
        inputStyle="box"
        labelStyle="stacked"
        @click="handleInputClick"
      />
    </div>
    <MbscPopup
      className="demo-date-filtering-popup"
      :responsive="respPopup"
      :isOpen="isPopupOpen"
      @close="handlePopupClose"
    >
      <div class="mbsc-grid mbsc-no-padding">
        <div class="mbsc-row">
          <div class="mbsc-col-sm-4 mbsc-push-sm-8 demo-date-filtering-dates">
            <div class="demo-date-filtering-inputs">
              <MbscSelect
                v-model="selected"
                :data="myData"
                :responsive="respSelect"
                label="Date range"
                inputStyle="box"
                labelStyle="stacked"
                @change="handleSelectChange"
              />
              <MbscInput
                ref="startInput"
                label="Start"
                inputStyle="box"
                labelStyle="stacked"
                :disabled="disableInput"
              />
              <MbscInput
                ref="endInput"
                label="End"
                inputStyle="box"
                labelStyle="stacked"
                :disabled="disableInput"
              />
            </div>
            <div class="demo-date-filtering-desktop-buttons mbsc-button-group-justified">
              <MbscButton className="apply-button" @click="applyClick">Apply</MbscButton>
              <MbscButton className="cancel-button" @click="cancelClick">Cancel</MbscButton>
            </div>
          </div>
          <div class="mbsc-col-sm-8 mbsc-pull-sm-4">
            <MbscDatepicker
              v-model="selectedDate"
              select="range"
              display="inline"
              pages="auto"
              returnFormat="iso8601"
              :startInput="startInput"
              :endInput="endInput"
              :controls="['calendar']"
              :showRangeLabels="false"
              :showOnClick="false"
              :showOnFocus="false"
              @change="handleCalendarChange"
            />
          </div>
        </div>
      </div>
    </MbscPopup>
  </MbscPage>
</template>

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

### SFC TS

```vue
<script setup lang="ts">
import {
  formatDate,
  MbscButton,
  MbscDatepicker,
  MbscInput,
  MbscPage,
  MbscPopup,
  MbscSelect,
  type MbscSelectChangeEvent,
  options,
  setOptions
} from '@mobiscroll/vue'
import type { MbscPopupOptions, MbscResponsiveOptions } from '@mobiscroll/vue'
import { onMounted, ref } from 'vue'

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

const now = new Date()
const day = now.getDay()
const monday = now.getDate() - day + (day === 0 ? -6 : 1)
const startDate = '2026-11-01T00:00'
const endDate = '2026-11-07T00:00'

const startInput = ref<any>(null)
const endInput = ref<any>(null)
const dateInput = ref<string>('')
const selected = ref<string>('custom')
const selectedDate = ref<any>([startDate, endDate])
const disableInput = ref<boolean>(false)
const isPopupOpen = ref<boolean>(false)

const respPopup: MbscResponsiveOptions<MbscPopupOptions> = {
  xsmall: {
    display: 'bottom',
    touchUi: true,
    buttons: [
      {
        text: 'Apply',
        handler: () => {
          const date = selectedDate.value

          changeInputValue(date[0], date[1] || date[0])
          isPopupOpen.value = true
        }
      },
      '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 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 handleSelectChange(event: MbscSelectChangeEvent) {
  const s = event.value

  if (s === 'custom') {
    disableInput.value = false
  } else {
    disableInput.value = true

    switch (s) {
      case 'today':
        selectedDate.value = ['2021-09-15T00:00', '2021-09-15T00:00']
        break
      case 'yesterday':
        selectedDate.value = ['2021-09-14T00:00', '2021-09-14T00:00']
        break
      case 'last-week':
        selectedDate.value = [
          new Date(now.getFullYear(), now.getMonth(), monday - 7),
          new Date(now.getFullYear(), now.getMonth(), monday - 1)
        ]
        break
      case 'last-month':
        selectedDate.value = ['2021-08-01T00:00', '2021-08-31T00:00']
        break
      case 'last-7-days':
        selectedDate.value = ['2021-09-09T00:00', '2021-09-15T00:00']
        break
      case 'last-30-days':
        selectedDate.value = ['2021-08-17T00:00', '2021-09-15T00:00']
        break
      default:
        break
    }
  }
  selected.value = s
}

function handleCalendarChange() {
  disableInput.value = false
}

function handleInputClick() {
  isPopupOpen.value = true
}

function changeInputValue(start: string, end: string) {
  const locale = options.locale || {}
  const dateFormat = locale.dateFormat || 'DD/MM/YYYY'

  dateInput.value =
    formatDate(dateFormat, new Date(start)) + ' - ' + formatDate(dateFormat, new Date(end))
}

function applyClick() {
  const date = selectedDate.value

  changeInputValue(date[0], date[1] || date[0])
  isPopupOpen.value = false
}

function cancelClick() {
  isPopupOpen.value = false
}

function handlePopupClose() {
  isPopupOpen.value = false
}

onMounted(() => {
  changeInputValue(startDate, endDate)
})
</script>

<template>
  <MbscPage>
    <div class="mbsc-form-group">
      <MbscInput
        v-model="dateInput"
        inputStyle="box"
        labelStyle="stacked"
        @click="handleInputClick"
      />
    </div>
    <MbscPopup
      className="demo-date-filtering-popup"
      :responsive="respPopup"
      :isOpen="isPopupOpen"
      @close="handlePopupClose"
    >
      <div class="mbsc-grid mbsc-no-padding">
        <div class="mbsc-row">
          <div class="mbsc-col-sm-4 mbsc-push-sm-8 demo-date-filtering-dates">
            <div class="demo-date-filtering-inputs">
              <MbscSelect
                v-model="selected"
                :data="myData"
                :responsive="respSelect"
                label="Date range"
                inputStyle="box"
                labelStyle="stacked"
                @change="handleSelectChange"
              />
              <MbscInput
                ref="startInput"
                label="Start"
                inputStyle="box"
                labelStyle="stacked"
                :disabled="disableInput"
              />
              <MbscInput
                ref="endInput"
                label="End"
                inputStyle="box"
                labelStyle="stacked"
                :disabled="disableInput"
              />
            </div>
            <div class="demo-date-filtering-desktop-buttons mbsc-button-group-justified">
              <MbscButton className="apply-button" @click="applyClick">Apply</MbscButton>
              <MbscButton className="cancel-button" @click="cancelClick">Cancel</MbscButton>
            </div>
          </div>
          <div class="mbsc-col-sm-8 mbsc-pull-sm-4">
            <MbscDatepicker
              v-model="selectedDate"
              select="range"
              display="inline"
              pages="auto"
              returnFormat="iso8601"
              :startInput="startInput"
              :endInput="endInput"
              :controls="['calendar']"
              :showRangeLabels="false"
              :showOnClick="false"
              :showOnFocus="false"
              @change="handleCalendarChange"
            />
          </div>
        </div>
      </div>
    </MbscPopup>
  </MbscPage>
</template>

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

## Implementation notes

- The custom popup wraps a `Popup` component containing an inline `Datepicker` set to `select: 'range'`, `display: 'inline'`, `showRangeLabels: false`, and `pages: 'auto'` so the calendar fills the available popup width.
- The `Popup`'s `responsive` option defines a breakpoint-based layout: a `custom` breakpoint config anchors the popup to the input (`display: 'anchored'`, `anchor: <input element>`, `anchorAlign: 'start'`) with no buttons on wider screens, and an `xsmall` config switches to `display: 'bottom'`, `touchUi: true`, with `Apply`/`cancel` buttons for narrow screens.
- Preset options are populated from a plain data array (`{ value, text }` pairs for Today/Yesterday/Last week/Last month/Last 7 days/Last 30 days/Custom) fed into a `Select` component; selecting a preset computes a `[start, end]` date pair and pushes it into the range picker's `value`.
- `returnFormat: 'iso8601'` on the `Datepicker` makes the range value returned as ISO 8601 date strings, which the preset logic also produces so both sources stay in the same format.
- Selecting `'Custom'` in the preset dropdown re-enables the Start/End inputs; any other preset disables them (they only display the computed range).
- `startInput`/`endInput` bind the calendar's start/end values to separate Start and End inputs - in React/Vue these take input refs, in Angular template reference variables, and in JS/jQuery selector strings - enabling users to type a date instead of only picking it from the calendar.
- `showOnClick: false` and `showOnFocus: false` on the inline `Datepicker` prevent it from also trying to open as its own anchored popup, since the surrounding custom `Popup` already controls visibility.
- The picker's `onChange` handler resets the preset selection back to `'custom'` whenever the user manually picks dates from the calendar, keeping the preset dropdown in sync with manual selection.

## 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-vue.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 vue
   ```

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 vue --trial`.

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

## Related demos

- Next: [Flight Booking](https://demo.mobiscroll.com/vue/range/flight-booking.md) - Build a flight booking date picker with the Vue range component — round-trip and one-way selection.
