Date picker
A date picker lets users pick a date from a calendar instead of typing it, for booking forms, deadlines and other date fields.
Overview
The date picker is Tabler's Datepicker plugin wrapped around Vanilla Calendar Pro. It is the same plugin, with the same options, methods and events, as the Datepicker in Bootstrap 6, so markup written for one works with the other. Tabler styles the calendar the plugin renders, so it follows your theme, your border radius and your dark mode.
Add data-bs-toggle="datepicker" to a text input. The calendar opens when the field gets focus, and the picked date is written back into the field, so it works in a form like any other text field.
<label class="form-label" for="datepicker-overview">Date</label>
<input type="text" class="form-control" id="datepicker-overview" data-bs-toggle="datepicker" autocomplete="off" placeholder="Select a date" />Installation
The plugin itself ships in tabler.js. The calendar it renders comes from Vanilla Calendar Pro, which you load separately. Install it with npm:
npm install vanilla-calendar-proyarn add vanilla-calendar-propnpm install vanilla-calendar-probun install vanilla-calendar-proIf you use a bundler, put the library on window before Tabler loads. The plugin reads window.VanillaCalendarPro and does nothing without it. Imports run before the rest of a file, so do it in a file of its own and import that one first:
// vanilla-calendar-global.js
import * as VanillaCalendarPro from 'vanilla-calendar-pro'
window.VanillaCalendarPro = VanillaCalendarPro
import './vanilla-calendar-global.js'
import '@tabler/core'
Or include it from a CDN, before tabler.js:
<script src="https://cdn.jsdelivr.net/npm/@tabler/[email protected]/dist/libs/vanilla-calendar-pro/index.js"></script>
You do not need the library's own stylesheet. Tabler styles the calendar in tabler.css.
Vanilla Calendar Pro 3.4 splits several months, the time picker, week numbers, popups and animation into extensions. Tabler registers every extension the loaded library exports, so options such as data-bs-display-months-count="2" work without importing them yourself.
Usage
Basic input
Use type="text", not type="date", so the browser's own picker does not open too. Add autocomplete="off" so the browser does not cover the calendar with old values.
<label class="form-label" for="datepicker-basic">Date</label>
<input type="text" class="form-control" id="datepicker-basic" data-bs-toggle="datepicker" autocomplete="off" placeholder="Select a date" />Icon input
Wrap the field in .input-icon and add a calendar icon, so users can see what the field is for at a glance. The popup lines up with the wrapper, not with the input inside it.
<label class="form-label" for="datepicker-icon">Date</label>
<div class="input-icon">
<input type="text" class="form-control" id="datepicker-icon" data-bs-toggle="datepicker" autocomplete="off" placeholder="Select a date" />
<span class="input-icon-addon">
<!-- Download SVG icon from http://tabler.io/icons/icon/calendar -->
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon">
<path d="M4 7a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12" />
<path d="M16 3v4" />
<path d="M8 3v4" />
<path d="M4 11h16" />
<path d="M11 15h1" />
<path d="M12 15v3" />
</svg>
</span>
</div>Initial value
A value in YYYY-MM-DD format is shown on load and the calendar opens on that month. You can also preselect dates with data-bs-selected-dates, which takes a JSON array.
<label class="form-label" for="datepicker-value">Birth date</label>
<input type="text" class="form-control" id="datepicker-value" data-bs-toggle="datepicker" autocomplete="off" value="1990-06-20" />Min and max dates
Limit the pickable dates with data-bs-date-min and data-bs-date-max. Dates outside the range are shown but cannot be picked.
<label class="form-label" for="datepicker-min-max">Event date (2026 only)</label>
<input type="text" class="form-control" id="datepicker-min-max" data-bs-toggle="datepicker" autocomplete="off" data-bs-date-min="2026-01-01" data-bs-date-max="2026-12-31" placeholder="Select a date in 2026" />Date range
Set data-bs-selection-mode="multiple-ranged" to pick a start and an end date. The calendar closes after the second date. The field shows both dates with a dash between them.
<label class="form-label" for="datepicker-range">Stay</label>
<input type="text" class="form-control" id="datepicker-range" data-bs-toggle="datepicker" autocomplete="off" data-bs-selection-mode="multiple-ranged" data-bs-selected-dates="["2026-06-10", "2026-06-18"]" placeholder="Select start and end" />Multiple dates
Set data-bs-selection-mode="multiple" to pick any number of dates. The calendar stays open until focus leaves it. The field lists the dates with commas.
<label class="form-label" for="datepicker-multiple">Days off</label>
<input type="text" class="form-control" id="datepicker-multiple" data-bs-toggle="datepicker" autocomplete="off" data-bs-selection-mode="multiple" placeholder="Select dates" />Two months
Add data-bs-display-months-count="2" to show two months side by side. This helps with ranges that cross a month boundary.
<label class="form-label" for="datepicker-two-months">Stay</label>
<input type="text" class="form-control" id="datepicker-two-months" data-bs-toggle="datepicker" autocomplete="off" data-bs-selection-mode="multiple-ranged" data-bs-display-months-count="2" placeholder="Select start and end" />First day of the week
The week starts on Monday. Set data-bs-first-weekday="0" to start it on Sunday.
<label class="form-label" for="datepicker-sunday">Date</label>
<input type="text" class="form-control" id="datepicker-sunday" data-bs-toggle="datepicker" autocomplete="off" data-bs-first-weekday="0" placeholder="Select a date" />Week numbers
Add data-bs-week-numbers="true" to show the week number in front of each row. Use it where people plan by week, for example delivery dates or sprints.
<label class="form-label" for="datepicker-week-numbers">Delivery date</label>
<input type="text" class="form-control" id="datepicker-week-numbers" data-bs-toggle="datepicker" autocomplete="off" data-bs-week-numbers="true" placeholder="Select a date" />Placement
The popup lines up with the left edge of the field. Set data-bs-placement to center, right or auto to change that.
<label class="form-label" for="datepicker-right">Right aligned</label>
<input type="text" class="form-control" id="datepicker-right" data-bs-toggle="datepicker" autocomplete="off" data-bs-placement="right" placeholder="Select a date" />Date format
The field shows the date in the format of data-bs-locale. Set data-bs-date-format to a pattern to write it the same way in every language. The pattern uses YYYY for the year, MM and M for the month, and DD and D for the day. The short tokens drop the leading zero. Everything else is copied as it is. Use iso as a shortcut for YYYY-MM-DD.
<div class="row g-3">
<div class="col-sm-6">
<label class="form-label" for="datepicker-format-iso">ISO date, Polish calendar</label>
<input type="text" class="form-control" id="datepicker-format-iso" data-bs-toggle="datepicker" autocomplete="off" data-bs-locale="pl-PL" data-bs-date-format="iso" value="2026-09-30" />
</div>
<div class="col-sm-6">
<label class="form-label" for="datepicker-format-pattern">Custom pattern</label>
<input type="text" class="form-control" id="datepicker-format-pattern" data-bs-toggle="datepicker" autocomplete="off" data-bs-date-format="DD/MM/YYYY" placeholder="DD/MM/YYYY" />
</div>
</div>A field with a pattern also reads its value in that pattern, so a form the server sends back keeps the picked date selected.
Button trigger
Any element can open the calendar, not only an input. A button shows the picked date as its text. Put the text in a child with data-bs-datepicker-display so an icon next to it stays.
<div class="btn-list">
<button type="button" class="btn" data-bs-toggle="datepicker">
<!-- Download SVG icon from http://tabler.io/icons/icon/calendar -->
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon">
<path d="M4 7a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12" />
<path d="M16 3v4" />
<path d="M8 3v4" />
<path d="M4 11h16" />
<path d="M11 15h1" />
<path d="M12 15v3" />
</svg>
<span data-bs-datepicker-display="true">Select date</span>
</button>
<button type="button" class="btn" data-bs-toggle="datepicker" data-bs-selection-mode="multiple-ranged">
<!-- Download SVG icon from http://tabler.io/icons/icon/calendar-event -->
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon">
<path d="M4 7a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2l0 -12" />
<path d="M16 3l0 4" />
<path d="M8 3l0 4" />
<path d="M4 11l16 0" />
<path d="M8 15h2v2h-2l0 -2" />
</svg>
<span data-bs-datepicker-display="true">Select range</span>
</button>
</div>To show the date somewhere else, pass the displayElement option when you create the picker in JavaScript.
Inline calendar
Add data-bs-inline="true" to render the calendar in place instead of a popup. Tabler creates inline calendars on page load, so they need no extra JavaScript. An inline calendar has no background or border of its own. Put it in a card to give it a surface.
<div class="card" style="width: fit-content">
<div class="card-body">
<div data-bs-toggle="datepicker" data-bs-inline="true"></div>
</div>
</div>Inline calendars take the same options as popups:
<div data-bs-toggle="datepicker" data-bs-inline="true" data-bs-selection-mode="multiple-ranged" data-bs-display-months-count="2"></div>To submit the picked dates with a form, put a hidden input inside the calendar. Tabler writes the dates to it in YYYY-MM-DD format, separated by commas.
<form onsubmit="return false">
<div data-bs-toggle="datepicker" data-bs-inline="true">
<input type="hidden" name="selected_date" />
</div>
<button type="submit" class="btn btn-primary mt-3">Submit</button>
</form>A popup field works the same way. Point data-bs-bound-input at a hidden input and give the name to that input, not to the field. The field shows the date in the visitor's language, and the form sends YYYY-MM-DD. When the hidden input already has a value, the picker selects that date on page load.
<form onsubmit="return false">
<label class="form-label" for="datepicker-bound">Due date</label>
<input type="text" class="form-control" id="datepicker-bound" data-bs-toggle="datepicker" autocomplete="off" data-bs-locale="pl-PL" data-bs-bound-input="#datepicker-bound-value" placeholder="Select a date" />
<input type="hidden" id="datepicker-bound-value" name="due_date" value="2026-09-30" />
<button type="submit" class="btn btn-primary mt-3">Submit</button>
</form>JavaScript
Via data attributes
Add data-bs-toggle="datepicker" and Tabler does the rest. Inputs open on focus, other elements open on click, and inline calendars and fields with a value, data-bs-selected-dates or data-bs-bound-input render on page load. Every option below can be set as a data-bs-* attribute, for example data-bs-selection-mode="multiple".
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (this: HTMLElement, event: Event) {
// Inputs open on focus, and inline calendars are always visible
if (this.tagName === 'INPUT' || this.dataset.bsInline === 'true' || this.dataset.tblrInline === 'true') {
return
}
event.preventDefault()
void (Datepicker.getOrCreateInstance(this) as Datepicker).toggle()
})
EventHandler.on(document, EVENT_FOCUSIN_DATA_API, SELECTOR_DATA_TOGGLE, function (this: HTMLElement) {
if (this.tagName !== 'INPUT') {
return
}
void (Datepicker.getOrCreateInstance(this) as Datepicker).show()
})
// Render on load what cannot wait for a focus or a click: inline calendars,
// and fields with preselected dates or a value, so they show it right away.
initAll(SELECTOR_DATA_TOGGLE, Datepicker, (element) => {
const { dataset } = element
return dataset.bsInline === 'true' || dataset.tblrInline === 'true' || 'bsSelectedDates' in dataset || 'tblrSelectedDates' in dataset || 'bsBoundInput' in dataset || 'tblrBoundInput' in dataset || Boolean((element as HTMLInputElement).value)
})Via JavaScript
Create the picker yourself when you need options that do not fit in an attribute, such as a date format or a callback:
const datepicker = new tabler.Datepicker(document.getElementById('my-date'), {
selectionMode: 'multiple-ranged',
firstWeekday: 0,
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
});
Options
| Name | Type | Default | Description |
|---|---|---|---|
dateMin |
string, number, Date | null |
First pickable date, as YYYY-MM-DD. |
dateMax |
string, number, Date | null |
Last pickable date, as YYYY-MM-DD. |
boundInput |
string, element | null |
Input that gets the picked dates as YYYY-MM-DD, separated by commas. An inline calendar uses a hidden input inside it. |
dateFormat |
string, object, function | null |
How the date is shown in the field. Pass a pattern such as 'DD.MM.YYYY', 'iso', Intl.DateTimeFormat options or a function(date, locale) that returns a string. |
displayElement |
string, element, boolean | null |
Element that shows the picked date. A button uses itself or its [data-bs-datepicker-display] child. Set false to show nothing. |
displayMonthsCount |
number | 1 |
Months shown side by side. |
firstWeekday |
number | 1 |
First day of the week: 0 is Sunday, 1 is Monday. |
inline |
boolean | false |
Render the calendar in place instead of a popup. |
locale |
string | 'default' |
Locale for month names and the date shown in the field, for example 'en-US' or 'de-DE'. A dateFormat pattern takes over the field. |
positionElement |
string, element | null |
Element the popup lines up with. Defaults to the .input-icon or .input-group wrapper, or the field itself. |
selectedDates |
array | [] |
Preselected dates as YYYY-MM-DD. |
selectionMode |
string | 'single' |
'single', 'multiple' or 'multiple-ranged'. |
placement |
string | 'left' |
Where the popup lines up with the field: 'left', 'center', 'right' or 'auto'. |
weekNumbers |
boolean | false |
Show the week number in front of each row. |
datepickerTheme |
string | null |
Force the popup's theme to 'light', 'dark' or 'auto'. By default it follows the nearest [data-bs-theme]. |
vcpOptions |
object | {} |
Any Vanilla Calendar Pro option, passed straight through. |
Use vcpOptions for everything the plugin can do that Tabler does not expose, for example disabling weekends or adding a time picker:
const datepicker = new tabler.Datepicker(element, {
vcpOptions: {
disableDatesPast: true,
disableWeekdays: [0, 6],
selectionTimeMode: 24,
},
});
Methods
| Method | Description |
|---|---|
show() |
Opens the popup. Does nothing for an inline calendar. |
hide() |
Closes the popup. |
toggle() |
Opens or closes the popup. |
getSelectedDates() |
Returns the picked dates as an array of YYYY-MM-DD strings. |
setSelectedDates(dates) |
Sets the picked dates. Takes an array of YYYY-MM-DD strings. |
dispose() |
Removes the calendar and the instance. |
calendar |
The Vanilla Calendar Pro instance, for anything the methods above do not cover. |
Datepicker.getInstance(element) |
Returns the instance for an element, or null. |
Datepicker.getOrCreateInstance(element) |
Returns the instance, or creates one. |
Events
| Event | Description |
|---|---|
show.bs.datepicker |
Fires before the popup opens. Call preventDefault() to keep it closed. |
shown.bs.datepicker |
Fires once the popup is open. |
hide.bs.datepicker |
Fires before the popup closes. Call preventDefault() to keep it open. |
hidden.bs.datepicker |
Fires once the popup is closed. |
change.bs.datepicker |
Fires when the user picks a date. The event has dates, an array of YYYY-MM-DD strings, and event, the click. |
document.getElementById('my-date').addEventListener('change.bs.datepicker', (event) => {
console.log(event.dates);
});
Accessibility
- Always add a
<label>linked to the input withforandid. A calendar icon alone does not say what the field is for. - Vanilla Calendar Pro marks the calendar up as a grid with
role="grid",role="row"androle="gridcell", labels every date and handles the arrow keys. Give an inline calendar an accessible name witharia-labelwhen nothing around it names it. - The popup closes on Escape, on a click outside, and when focus moves to something outside the calendar, so keyboard users are never trapped in it.
- Users can still type a date into the field. The picker is a shortcut, not the only way to set the value.
- The calendar respects
prefers-reduced-motionand drops its animations for users who ask for that.
Migration from Litepicker
Tabler 1.5 and older used Litepicker, which has been archived since 2023. It is deprecated: dist/libs/litepicker and its styles in tabler-vendors.css stay until 2.0, so existing pages keep working. To move over, load Vanilla Calendar Pro instead and switch the markup as shown below.
| Litepicker | Datepicker |
|---|---|
new Litepicker({ element }) |
data-bs-toggle="datepicker" on the element |
inlineMode: true |
data-bs-inline="true" |
singleMode: false |
data-bs-selection-mode="multiple-ranged" |
minDate / maxDate |
data-bs-date-min / data-bs-date-max |
numberOfMonths: 2 |
data-bs-display-months-count="2" |
format: 'DD.MM.YYYY' |
data-bs-date-format="DD.MM.YYYY" (tokens YYYY, MM, M, DD, D) |
picker.on('selected', …) |
change.bs.datepicker event |
CSS
Sass variables
Override these variables before importing Tabler to change the defaults. Source: core/scss/_variables.scss.
$datepicker-calendar-padding: 0;
$datepicker-calendar-font-size: $font-size-sm;
$datepicker-calendar-header-font-weight: $font-weight-medium;
$datepicker-calendar-day-size: 2rem;
$datepicker-calendar-day-font-size: $font-size-sm;
$datepicker-calendar-range-bg-opacity: 10%;
$datepicker-padding: 1rem;
$datepicker-bg: $dropdown-bg;
$datepicker-border-radius: $dropdown-border-radius;
$datepicker-box-shadow: $dropdown-box-shadow;
$datepicker-min-width: 280px;
