---
title: "Javascript Range Date Validation Example | Mobiscroll"
component: "Range"
framework: "JavaScript"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/javascript/range/disabled-invalid-values"
---

# Date Validation - JavaScript Range demo

Disable specific dates, date ranges, or recurring dates in the Javascript date range picker to guide users toward valid selections. Plain JavaScript API.

- [Live demo](https://demo.mobiscroll.com/javascript/range/disabled-invalid-values)
- [View code on GitHub](https://github.com/acidb/mobiscroll-demos-javascript/tree/main/src/demos/datepicker/range/disabled-invalid-values)
- This demo in other frameworks: [jQuery](https://demo.mobiscroll.com/jquery/range/disabled-invalid-values.md), [Angular](https://demo.mobiscroll.com/angular/range/disabled-invalid-values.md), [React](https://demo.mobiscroll.com/react/range/disabled-invalid-values.md), [Vue](https://demo.mobiscroll.com/vue/range/disabled-invalid-values.md)

## Description

Enforcing validation is essential to a great UX. First make sure to have the [min & max values right](https://demo.mobiscroll.com/javascript/range/min-max-restrictions) and then work your way through disabled values.

Depending on your situation, you have two options:

- **Set invalid** - set the invalids through the [`invalid`](https://mobiscroll.com/docs/javascript/datepicker/api#opt-invalid) option.
- **Set valid** - set the valids through the [`valid`](https://mobiscroll.com/docs/javascript/datepicker/api#opt-valid) option.

Both the [`invalid`](https://mobiscroll.com/docs/javascript/datepicker/api#opt-invalid) and [`valid`](https://mobiscroll.com/docs/javascript/datepicker/api#opt-valid) options support the following:

- **Exact dates** - Passing exact values like: `'2020-05-20'` will disable/enable the specific day
- **Date ranges** - Passing `start` and `end` value pairs will disable/enable specific days that fall into that range
- **Recurring dates** - Passing recurrence rules as objects or in RRULE string format will be parsed. For [more information on recurrence check out the rule generator](https://demo.mobiscroll.com/javascript/range/recurring-values)

Exact dates and the start/end pairs can be passed as [JS date objects, ISO date strings or Moment.js objects](https://demo.mobiscroll.com/javascript/range/date-object-ISO-8601-moment). Having invalids set up correctly not just enhances the UX, but improves performance.

The passed date-times can also contain timezone data which requires a [`timezonePlugin`](https://mobiscroll.com/docs/javascript/datepicker/api#opt-timezonePlugin) to be interpreted correctly.

**Using date-times across different timezones?** [Learn more about timezone support →](https://demo.mobiscroll.com/javascript/range/setting-the-picker-timezone)

## What this demo shows

- Configures invalid or valid dates in a date range picker to control which dates are available for selection.
- **Disabled dates within a range** Allows disabled dates to appear inside a selected range while preventing them from being selected as the range start or end.
- **Disabled range end** Optionally allows a disabled date to be selected as the range end, such as an unavailable checkout date in an accommodation booking flow.
- **Exact dates** Disables specific dates or defines specific valid dates while treating all remaining dates as unavailable.
- **Recurring dates** Disables repeating dates, including weekends, holidays, or the first and last day of each month.
- **Date ranges** Disables complete ranges to block longer periods from selection.
- **Combined rules** Applies multiple invalid-date rules to represent more complex availability and booking restrictions.
- **Inline date range picker** Embeds the picker directly in the page without an input by using the inline display mode.
- **Start and end inputs** Displays clickable start and end inputs above the calendar. The active input has a white background, the inactive input has a gray background, and a clear button appears after a range is selected.
- **Header navigation** Opens the month and year picker from the label in the upper-left corner. Previous and next arrow buttons navigate between months.
- **Month view** Shows a fixed weekday header with abbreviated day names from Sunday through Saturday and dates arranged in a grid.
- **Month navigation** Supports dragging the calendar left or right to move between months.
- **Day cell states** Uses a muted appearance for disabled dates, a gray background on hover, and blue highlighting for selected dates. The current date is highlighted by default and remains blue when another date is selected.
- **Range selection** Uses the first selected date as the range start and the second selected date as the range end.
- **Adjacent months** Shows dates from the previous and next months in a muted style. Selecting one navigates to its corresponding month.

## Best for

- **Accommodation booking** Allowing an unavailable date to serve as the checkout date while preventing it from being used as the start of a stay.
- **Reservations and rentals** Enforcing availability windows, blackout periods, and recurring restrictions when users select a date range.
- **Appointment scheduling** Preventing bookings on unavailable dates or during blocked periods.
- **Leave management** Restricting date ranges around weekends, holidays, and other recurring non-working days.
- **Complex availability rules** Combining exact dates, recurring dates, and disabled ranges in a single date selection flow.

## Tutorial

### Allow disabled dates inside ranges

- [x] Only start and end dates can't be invalids

### Allow end date selection on disabled dates

- [ ] Be able to select a disabled date for the range end (useful for accommodation booking)

### Do you want to

- (x) Set invalid values
- ( ) Set valid values

### Disable exact dates

- [x] Disable these days

`2026-10-23, 2026-10-26`

### Disable recurring dates

- [x] Disable weekends
- [ ] Disable holidays (Christmas Eve, New Years)
- [ ] Disable the first and last of every month

### Disable one or more date ranges

- [x] Disable this range

`2026-10-28 - 2026-11-04`

```js
mobiscroll.datepicker('#datepicker', {
    controls: ['calendar'],
    select: 'range',
    inRangeInvalid: true,
    rangeEndInvalid: false,
    invalid: [
		'2026-10-23',
		'2026-10-26',
		{
			start: '2026-10-28T18:02:00.000Z',
			end: '2026-11-04T18:02:00.000Z'
		},
		{
			recurring: {
				repeat: 'weekly',
				weekDays: 'SA,SU'
			}
		}
	]
});
```

With "Set valid values":

```js
mobiscroll.datepicker('#datepicker', {
    controls: ['calendar'],
    select: 'range',
    inRangeInvalid: true,
    rangeEndInvalid: false
    valid: [
		'2026-10-23',
		'2026-10-26',
		{
			start: '2026-10-28T18:02:00.000Z',
			end: '2026-11-04T18:02:00.000Z'
		},
		{
			recurring: {
				repeat: 'weekly',
				weekDays: 'MO, TU, WE, TH, FR'
			}
		}
	]
});
```

With "Disable these days" off:

```js
mobiscroll.datepicker('#datepicker', {
    controls: ['calendar'],
    select: 'range',
    inRangeInvalid: true,
    rangeEndInvalid: false,
    invalid: [
		{
			start: '2026-10-28T18:02:00.000Z',
			end: '2026-11-04T18:02:00.000Z'
		},
		{
			recurring: {
				repeat: 'weekly',
				weekDays: 'SA,SU'
			}
		}
	]
});
```

With "Disable weekends" off:

```js
mobiscroll.datepicker('#datepicker', {
    controls: ['calendar'],
    select: 'range',
    inRangeInvalid: true,
    rangeEndInvalid: false,
    invalid: [
		'2026-10-23',
		'2026-10-26',
		{
			start: '2026-10-28T18:02:00.000Z',
			end: '2026-11-04T18:02:00.000Z'
		}
	]
});
```

With "Disable holidays (Christmas Eve, New Years)" on:

```js
mobiscroll.datepicker('#datepicker', {
    controls: ['calendar'],
    select: 'range',
    inRangeInvalid: true,
    rangeEndInvalid: false,
    invalid: [
		'2026-10-23',
		'2026-10-26',
		{
			recurring: {
				repeat: 'yearly',
				day: 24,
				month: 12
			}
		},
		{
			recurring: {
				repeat: 'yearly',
				day: 31,
				month: 12
			}
		},
		{
			start: '2026-10-28T18:02:00.000Z',
			end: '2026-11-04T18:02:00.000Z'
		},
		{
			recurring: {
				repeat: 'weekly',
				weekDays: 'SA,SU'
			}
		}
	]
});
```

With "Disable the first and last of every month" on:

```js
mobiscroll.datepicker('#datepicker', {
    controls: ['calendar'],
    select: 'range',
    inRangeInvalid: true,
    rangeEndInvalid: false,
    invalid: [
		'2026-10-23',
		'2026-10-26',
		{
			recurring: {
				repeat: 'monthly',
				day: 1
			}
		},
		{
			recurring: {
				repeat: 'monthly',
				day: -1
			}
		},
		{
			start: '2026-10-28T18:02:00.000Z',
			end: '2026-11-04T18:02:00.000Z'
		},
		{
			recurring: {
				repeat: 'weekly',
				weekDays: 'SA,SU'
			}
		}
	]
});
```

With "Disable this range" off:

```js
mobiscroll.datepicker('#datepicker', {
    controls: ['calendar'],
    select: 'range',
    inRangeInvalid: true,
    rangeEndInvalid: false,
    invalid: [
		'2026-10-23',
		'2026-10-26',
		{
			recurring: {
				repeat: 'weekly',
				weekDays: 'SA,SU'
			}
		}
	]
});
```

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/javascript/datepicker/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/javascript/datepicker/api#opt-themeVariant) - option
- [`controls`](https://mobiscroll.com/docs/javascript/datepicker/api#opt-controls) - option
- [`select`](https://mobiscroll.com/docs/javascript/datepicker/api#opt-select) - option
- [`display`](https://mobiscroll.com/docs/javascript/datepicker/api#opt-display) - option
- [`invalid`](https://mobiscroll.com/docs/javascript/datepicker/api#opt-invalid) - option

## Code

### JS

```js
mobiscroll.setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

mobiscroll.datepicker('#demo', {
  controls: ['calendar'],
  select: 'range',
  display: 'inline',
  invalid: [
    '2020-02-12',
    '2020-05-20',
    {
      recurring: {
        repeat: 'weekly',
        weekDays: 'SA,SU',
      },
    },
    {
      recurring: {
        repeat: 'yearly',
        day: 24,
        month: 12,
      },
    },
    {
      recurring: {
        repeat: 'yearly',
        day: 31,
        month: 12,
      },
    },
    {
      recurring: {
        repeat: 'monthly',
        day: 1,
      },
    },
    {
      recurring: {
        repeat: 'monthly',
        day: -1,
      },
    },
    {
      start: '2020-03-15',
      end: '2020-03-30',
    },
    {
      start: '2020-07-05',
      end: '2020-08-20',
    },
  ],
});
```

### HTML

```html
<div id="demo"></div>
```

## Implementation notes

- Set `select: 'range'` with `controls: ['calendar']` and `display: 'inline'`.
- Pass the `invalid` array (or `valid` array - only one is active at a time; set the other to `undefined` to clear it) with entries in any of these shapes: exact date strings (`'2021-03-02'`), a date range (`{ start, end }`), or a recurring rule (`{ recurring: { repeat: 'weekly' | 'yearly' | 'monthly', weekDays, day, month } }`) - e.g. `{ recurring: { repeat: 'weekly', weekDays: 'SA,SU' } }` for weekends, `{ recurring: { repeat: 'yearly', day: 24, month: 12 } }` for a fixed yearly date, or `{ recurring: { repeat: 'monthly', day: -1 } }` for the last day of every month.
- Set `inRangeInvalid: true` so only the range's start/end need to avoid invalid dates while dates in between can be invalid; set `inRangeInvalid: false` and `rangeEndInvalid: true` instead to allow the range end specifically to land on an invalid date (e.g. an accommodation checkout date) while the start still cannot.
- Update the active rule set at runtime with `invalidDate.setOptions({ invalid: [...], valid: undefined })` (or the reverse to switch to `valid`), and `invalidDate.setOptions({ inRangeInvalid: checked })` / `{ rangeEndInvalid: checked }` to toggle those flags. React/Angular/Vue: update the bound `invalid`/`valid`/`inRangeInvalid`/`rangeEndInvalid` props/state instead of calling `setOptions`.
- Angular binds these as `[invalid]`/`[valid]`/`[inRangeInvalid]`/`[rangeEndInvalid]`; Vue binds them as `:invalid`/`:valid`/`:inRangeInvalid`/`:rangeEndInvalid`; React passes them as props (`invalid`, `valid`, `inRangeInvalid`, `rangeEndInvalid`); JS/jQuery pass them in the options object at init and update via `setOptions`.

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

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

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

## Related demos

- Previous: [Date Restrictions](https://demo.mobiscroll.com/javascript/range/min-max-restrictions.md) - Set min and max date boundaries in the Javascript date range picker to restrict selectable dates and times — supports date-only and date-time modes.
- Next: [Recurring Rules](https://demo.mobiscroll.com/javascript/range/recurring-values.md) - Configure recurring rules in the Javascript range picker — set daily, weekly, monthly, or yearly patterns, define stop conditions, and exclude specific dates.
