Fast, Customizable Date Picker in Vanilla JS & jQuery - Air Datepicker

File Size: 242 KB
Views Total: 29213
Last Update:
Publish Date:
Official Website: Go to website
License: MIT
   
Fast, Customizable Date Picker in Vanilla JS & jQuery - Air Datepicker

Air Datepicker is a pure JavaScript date picker library that handles single dates, date ranges, multi-date selection, and time picking.

It runs on ES6, uses native CSS variables for theming, and works across all modern browsers.

Version 3 rewrites the entire library from scratch, removes the jQuery requirement, and adopts Unicode Technical Standard #35 for date formatting.

You can also download Air Datepicker v2 for legacy jQuery-based codebases.

Features:

  • Native CSS variable support for full theme customization.
  • Single date, multi-date, and date range selection modes.
  • Three calendar views: days, months, and years.
  • Built-in timepicker with configurable 12-hour and 24-hour display.
  • Inline display mode that keeps the calendar permanently visible.
  • Keyboard navigation with shortcut keys for month, year, and decade traversal.
  • Configurable minimum and maximum selectable dates.
  • Custom action buttons with a built-in "Today" and "Clear" option.
  • Per-cell rendering control for disabling specific dates or adding custom HTML.
  • Full localization through swappable locale objects (33+ languages included).
  • Draggable date range adjustment after initial selection.
  • TypeScript support with full type definitions included.

Use Cases:

  • Booking forms that need a date range picker for check‑in and check‑out.
  • Admin dashboards where a time picker attaches to a date field for scheduling tasks.
  • Inline calendar widgets that display available dates for appointment selection.
  • Reports filters that accept multiple dates to compare metrics across days.

How to use it (Vanilla JS Version):

1. Install Air Datepicker via npm, and import both the JS module and the CSS:

# NPM
npm i air-datepicker
// Core
import AirDatepicker from 'air-datepicker';
// Local
import localeEn from 'air-datepicker/locale/en';
// Stylesheet
import 'air-datepicker/air-datepicker.css';

2. For non-module projects, load the following files directly in your HTML document:

<!-- Load the stylesheet in <head> -->
<link rel="stylesheet" href="dist/air-datepicker.css">

<!-- Load the script before closing </body> -->
<script src="dist/air-datepicker.js"></script>
<script src="dist/local/en.js"></script>

3. Attach the datepicker to any input element or container:

// Initialize on an input field
new AirDatepicker('#booking-date');

4. For a date range picker with a timepicker and action buttons:

new AirDatepicker('#trip-dates', {
  range: true,
  timepicker: true,
  buttons: ['today', 'clear'] 
});

5. Use onRenderCell to disable specific dates or inject custom HTML into individual cells:

new AirDatepicker('#availability', {
  onRenderCell({ date, cellType }) {
    // Disable every Monday in the day view
    if (cellType === 'day' && date.getDay() === 1) {
      return {
        disabled: true,
        classes: 'unavailable-day',
        attrs: { title: 'Mondays are unavailable' }
      };
    }
  }
});

6. Override the default dropdown positioning with a custom function:

new AirDatepicker('#meeting-date', {
  autoClose: true,
  position({ $datepicker, $target, $pointer }) {
    // Center the calendar below the input field
    let rect = $target.getBoundingClientRect();
    let dpW  = $datepicker.clientWidth;

    $datepicker.style.left = `${rect.x + rect.width / 2 - dpW / 2}px`;
    $datepicker.style.top  = `${rect.y + rect.height + window.scrollY + 8}px`;

    // Hide the default pointer arrow
    $pointer.style.display = 'none';
  }
});

7. All configuration options to customize the date picker:

  • classes (string): Adds extra CSS classes to the calendar element.
  • inline (boolean, default false): Keeps the calendar permanently visible.
  • locale (object, default ru): Sets the calendar language. Pass a locale object from air-datepicker/locale/.
  • startDate (Date | string | number, default new Date()): Sets the initial view date when the calendar opens.
  • firstDay (number): Overrides the locale's first day of the week. 0 is Sunday, 6 is Saturday.
  • weekends (array, default [6, 0]): Indexes of days marked as weekends with the .weekend class.
  • isMobile (boolean, default false): Renders the calendar as a modal dialog with enlarged dimensions.
  • visible (boolean, default false): Shows the calendar immediately on initialization.
  • dateFormat (string | function): Date display format using Unicode Technical Standard #35 tokens, or a custom function returning a string.
  • altField (string | DOMNode): A secondary input field that receives the date in altFieldDateFormat.
  • altFieldDateFormat (string | function, default "T"): The format written to the alternate field.
  • toggleSelected (boolean | function, default true): Clicking an already-selected date removes it. Pass a function for conditional behavior.
  • keyboardNav (boolean, default true): Activates keyboard navigation on text input elements.
  • selectedDates (Date[] | string[] | number[]): Pre-selected dates on initialization. Accepts mixed types.
  • container (string | HTMLElement): A custom parent element for the calendar DOM node.
  • position (string | function, default "bottom left"): Calendar position relative to the input, or a custom positioning function.
  • view (string, default "days"): Initial calendar view. Options: "days", "months", "years".
  • minView (string, default "days"): The least granular view the user can reach. Useful for month-only pickers.
  • showOtherMonths (boolean, default true): Shows dates from adjacent months in the days view.
  • selectOtherMonths (boolean, default true): Allows selection of dates from adjacent months.
  • moveToOtherMonthsOnSelect (boolean, default true): Navigates to the adjacent month when a date from it is selected.
  • minDate (Date | string | number): The earliest selectable date.
  • maxDate (Date | string | number): The latest selectable date.
  • disableNavWhenOutOfRange (boolean, default true): Deactivates the previous/next navigation buttons when at the minDate or maxDate boundary.
  • multipleDates (boolean | number, default false): true allows unlimited selections; a number caps the count.
  • multipleDatesSeparator (string, default ", "): Text separator between dates in the input field.
  • range (boolean, default false): Activates date range selection mode.
  • dynamicRange (boolean, default true): Allows dragging selected range endpoints to adjust the range after selection.
  • buttons (string | object | array | false, default false): Action buttons at the bottom of the calendar. Built-in values: "today", "clear". Pass an object to define a custom button.
  • monthsField (string, default "monthsShort"): The locale field used for month names in the months view.
  • showEvent (string, default "focus"): The DOM event that triggers the calendar to open.
  • autoClose (boolean): Hides the calendar after a date is selected.
  • prevHtml (string): Custom HTML for the Previous navigation button.
  • nextHtml (string): Custom HTML for the Next navigation button.
  • navTitles (object): Title templates for each view. Accepts HTML, date tokens, or a function returning a string.
  • fixedHeight (boolean, default false): Fixes the calendar height to always show 6 weeks.
  • timepicker (boolean, default false): Shows the time picker below the calendar.
  • onlyTimepicker (boolean, default false): Shows only the time picker, no date grid.
  • dateTimeSeparator (string, default " "): Separator between the date and time portions in the output.
  • timeFormat (string): Time format string using Unicode tokens. Passing a 12-hour format token auto-switches the time slider to AM/PM mode.
  • minHours (number, default 0): Minimum selectable hour.
  • maxHours (number, default 24): Maximum selectable hour.
  • minMinutes (number, default 0): Minimum selectable minute.
  • maxMinutes (number, default 59): Maximum selectable minute.
  • hoursStep (number, default 1): Step increment for the hours slider.
  • minutesStep (number, default 1): Step increment for the minutes slider.
new AirDatepicker('#meeting-date', {
  classes: '',
  inline: false,
  locale: localeRu,
  startDate: new Date(),
  firstDay: '',
  weekends: [6, 0],
  dateFormat: '',
  altField: '',
  altFieldDateFormat: 'T',
  toggleSelected: true,
  keyboardNav: true,
  selectedDates: false,
  container: '',
  isMobile: false,
  visible: false,

  position: 'bottom left',
  offset: 12,

  view: consts.days,
  minView: consts.days,

  showOtherMonths: true,
  selectOtherMonths: true,
  moveToOtherMonthsOnSelect: true,

  showOtherYears: true,
  selectOtherYears: true,
  moveToOtherYearsOnSelect: true,

  minDate: '',
  maxDate: '',
  disableNavWhenOutOfRange: true,

  multipleDates: false, // Boolean or Number
  multipleDatesSeparator: ', ',
  range: false,
  dynamicRange: true,
  buttons: false,
  monthsField: 'monthsShort',

  showEvent: 'focus',
  autoClose: false,
  fixedHeight: false,

  // navigation
  prevHtml: '<svg><path d="M 17,12 l -5,5 l 5,5"></path></svg>',
  nextHtml: '<svg><path d="M 14,12 l 5,5 l -5,5"></path></svg>',
  navTitles: {
      days: 'MMMM, <i>yyyy</i>',
      months: 'yyyy',
      years: 'yyyy1 - yyyy2'
  },

  timepicker: false,
  onlyTimepicker: false,
  dateTimeSeparator: ' ',
  timeFormat: '',
  minHours: 0,
  maxHours: 24,
  minMinutes: 0,
  maxMinutes: 59,
  hoursStep: 1,
  minutesStep: 1,
});

8. Date/time format tokens:

Token Output
T Timestamp in milliseconds
E Short day name (from locale daysShort)
EEEE Full day name (from locale days)
d Day of month
dd Day of month, zero-padded
M Month number
MM Month number, zero-padded
MMM Short month name
MMMM Full month name
yy Two-digit year
yyyy Full year
yyyy1 First year of decade
yyyy2 Last year of decade
h Hours, 12-hour
hh Hours, 12-hour, zero-padded
H Hours, 24-hour
HH Hours, 24-hour, zero-padded
m Minutes
mm Minutes, zero-padded
aa AM/PM, lowercase
AA AM/PM, uppercase

9. API methods:

// Show the calendar programmatically
datepicker.show();

// Hide the calendar programmatically
datepicker.hide();

// Navigate to the next month, year, or decade (depends on current view)
datepicker.next();

// Navigate to the previous month, year, or decade
datepicker.prev();

// Select a single date or an array of dates
// opts.updateTime: true sets the timepicker to the passed date's time
// opts.silent: true skips the onSelect callback
datepicker.selectDate(new Date('2025-09-01'), { updateTime: true, silent: false });

// Deselect a specific date
datepicker.unselectDate(new Date('2025-09-01'));

// Clear all selected dates
// opts.silent: true skips the onSelect callback
datepicker.clear({ silent: false });

// Format a date using any supported token string
// Returns the formatted string
let formatted = datepicker.formatDate(new Date('2025-09-01'), 'dd/MM/yyyy');

// Destroy the calendar and remove all event listeners from the target element
datepicker.destroy();

// Update one or more options after initialization
// params.silent: true skips onSelect and onChangeView callbacks
datepicker.update({ maxDate: new Date('2025-12-31') }, { silent: false });

// Switch to a specific view (days, months, years)
// params.silent: true skips onChangeView
datepicker.setCurrentView('months', { silent: false });

// Set the displayed date without selecting it (navigates to that month/year)
datepicker.setViewDate(new Date('2026-01-01'));

// Move focus to a specific calendar cell
// opts.viewDateTransition: true updates the view if the date is outside the current one
datepicker.setFocusDate(new Date('2025-09-15'), { viewDateTransition: true });

// Switch to the next view type (days -> months -> years)
datepicker.up();

// Switch to the previous view type (years -> months -> days)
datepicker.down();

// Get all dates currently displayed in the calendar for a given view
// Returns an array of Date objects
let visibleDates = datepicker.getViewDates('days');

// Disable one or more dates to prevent selection
datepicker.disableDate(new Date('2025-09-10'));
datepicker.disableDate(['2025-09-10', '2025-09-11', new Date('2025-09-12')]);

// Re-enable previously disabled dates
datepicker.enableDate(new Date('2025-09-10'));

10. Callback functions.

// Fires when a date is selected or deselected
// Receives: date (Date or Date[]), formattedDate (string or string[]), datepicker instance
new AirDatepicker('#reservation', {
  onSelect({ date, formattedDate, datepicker }) {
      console.log('Selected:', formattedDate);
  }
});

// Fires before a cell is selected; return false to block the selection
new AirDatepicker('#reservation', {
  onBeforeSelect({ date, datepicker }) {
      // Block weekends from being selected
      const day = date.getDay();
      return day !== 0 && day !== 6;
  }
});

// Fires when the user navigates to a different month, year, or decade
new AirDatepicker('#reservation', {
  onChangeViewDate({ month, year, decade }) {
      console.log('Viewing:', year, month);
  }
});

// Fires when switching between days, months, and years views
new AirDatepicker('#reservation', {
  onChangeView(view) {
      console.log('View changed to:', view);
  }
});

// Fires for every cell render; return an object to customize the cell
new AirDatepicker('#reservation', {
  onRenderCell({ date, cellType, datepicker }) {
      // Return html, classes, disabled, or attrs to customize
  }
});

// Fires when the calendar starts to appear (isFinished: false)
// and again after the animation completes (isFinished: true)
new AirDatepicker('#reservation', {
  onShow(isFinished) {
      if (isFinished) console.log('Calendar visible');
  }
});

// Fires when the calendar starts to hide and again after it finishes
new AirDatepicker('#reservation', {
  onHide(isFinished) {
      if (isFinished) console.log('Calendar hidden');
  }
});

// Fires when a user clicks a day name header
// dayIndex: 0 = Sunday, 6 = Saturday
new AirDatepicker('#reservation', {
  onClickDayName({ dayIndex, datepicker }) {
      console.log('Day column clicked:', dayIndex);
  }
});

// Fires when a calendar cell receives keyboard or mouse focus
new AirDatepicker('#reservation', {
  onFocus({ date, datepicker }) {
      console.log('Focused on:', date);
  }
});

11. Instance properties:

  • $datepicker (HTMLDivElement): The calendar's root DOM element.
  • $el (HTMLInputElement): The target element on which the datepicker was initialized.
  • viewDate (Date): The date currently displayed in the calendar header.
  • currentView (string): The active view: "days", "months", or "years".
  • selectedDates (Date[]): All currently selected dates as an array.
  • focusDate (Date | false): The date cell with current keyboard focus.
  • visible (boolean): true when the calendar is open.
  • disabledDates (Set<string>): All currently disabled dates as a Set of date strings.
  • isDestroyed (boolean): true after destroy() has been called.

How to use it (jQuery Version):

1. Include the jQuery library together with the jQuery Air Datepicker plugin's CSS and JS files on your webpage.

<link href="dist/css/datepicker.css" rel="stylesheet">
<script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="dist/js/datepicker.js"></script>

2. Adding the CSS class 'datepicker-here' to the target element will initialize the date picker plugin automatically. And you can pass all options via data-OPTION attributes on the html element.

<input type='text' class='datepicker-here' 
       data-language='en'
       data-position='right top'
>

3. You can also initialize the plugin manually and all the options can be passed to datepicker() method as an object.

$('#selector').datepicker({

  // inline mode
  inline: false,

  // additional CSS class
  classes: '',

  // language
  language: 'ru',

  // start date
  startDate: new Date(),

  // first day
  firstDay: '',

  // array of day's indexes
  weekends: [6, 0],

  // custom date format
  dateFormat: '',

  // Alternative text input. Use altFieldDateFormat for date formatting.
  altField: '',

  // Date format for alternative field.
  altFieldDateFormat: '@',

  // remove selection when clicking on selected cell
  toggleSelected: true,

  // keyboard navigation
  keyboardNav: true,

  // position
  position: 'bottom left',
  offset: 12,

  // days, months or years
  view: 'days',
  minView: 'days',

  showOtherMonths: true,
  selectOtherMonths: true,
  moveToOtherMonthsOnSelect: true,

  showOtherYears: true,
  selectOtherYears: true,
  moveToOtherYearsOnSelect: true,

  minDate: '',
  maxDate: '',
  disableNavWhenOutOfRange: true,

  multipleDates: false, // Boolean or Number
  multipleDatesSeparator: ',',
  range: false,

  // display today button
  todayButton: false,

  // display clear button
  clearButton: false,

  // Event type
  showEvent: 'focus',

  // auto close after date selection
  autoClose: false,

  // navigation
  monthsFiled: 'monthsShort',
  prevHtml: '<svg><path d="M 17,12 l -5,5 l 5,5"></path></svg>',
  nextHtml: '<svg><path d="M 14,12 l 5,5 l -5,5"></path></svg>',
  navTitles: {
      days: 'MM, <i>yyyy</i>',
      months: 'yyyy',
      years: 'yyyy1 - yyyy2'
  },

  // timepicker
  timepicker: false,
  onlyTimepicker: false,
  dateTimeSeparator: ' ',
  timeFormat: '',
  minHours: 0,
  maxHours: 24,
  minMinutes: 0,
  maxMinutes: 59,
  hoursStep: 1,
  minutesStep: 1,

  // callback events
  onSelect: '',
  onShow: '',
  onHide: '',
  onChangeMonth: '',
  onChangeYear: '',
  onChangeDecade: '',
  onChangeView: '',
  onRenderCell: ''

})

4. Customize languages.

Datepicker.language['en'] = {
    days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
    daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    daysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
    months: ['January','February','March','April','May','June', 'July','August','September','October','November','December'],
    monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    today: 'Today',
    clear: 'Clear',
    dateFormat: 'mm/dd/yy',
    firstDay: 0
};

Alternatives:

Changelog:

v3.6.0 (2026-04-24)

  • Rewritten in ES6.
  • Updated doc & demo.

v2.2.3 (2016-09-26)

  • fixed min,max dates in decade mode

v2.2.2 (2016-09-21)

  • fixed min,max dates handling

v2.2.1 (2016-09-18)

  • changed RegExp for recognizing date parts
  • changed jquery version dependency

v2.21.0 (2016-08-29)

  • added onlyTimepicker option
  • added onShow and onHide callbacks
  • added VERSION field to plugin's prototype
  • now for selecting same date in range mode, you should set {toggleSelected: false}
  • fixed dateFormat method (fixed wrong month name in Hungarian language)
  • fixed second call of onRenderCallback
  • fixed _getCell() throwing exception
  • new language: sk

v2.1.0 (2016-07-03)

  • added possibility to select single date when {range: true}
  • added support of 12 hours mode in altFieldDateFormat
  • improved work with minDate and maxDate when {timepicker: true}
  • fixed wrong class adding when {range: true}
  • new languages:

v2.0.2 (2016-05-20)

  • fixed dates array in onSelect callback

v2.0.1 (2016-05-16)

  • added timepicker 
  • added possibility to set Date in todayButton
  • global variable Datepicker has been removed, now all placed in $.fn.datepicker
  • improved selectDate method, now one can pass an array of dates to select
  • added npm package
  • fixed issue caused by placeholder on readonly inputs in IE
  • fixed issue when range is true and first selected date is bigger than second
  • added new languages

v1.2.1 (2016-01-24)

  • tests added
  • fixed if '0' is passed to 'firstDay'
  • fixed 'showOtherYears' option
  • fixed 'onSelect' event, when 'range' is true
  • fixed case when 'range' and 'multipleDates' both set to true

2015-12-10

  • fix removeDate when 1 range date is selected

2015-12-04

  • added keyboard navigation

This awesome jQuery plugin is developed by t1m0n. For more Advanced Usages, please check the demo page or visit the official website.