Bootstrap Datepicker: 16 Examples Without a Plugin

Search for a Bootstrap datepicker and the results mix several different tools under one name. The original, bootstrap-datepicker from uxsolutions, still requires jQuery, which Bootstrap dropped in 5.0, and Bootstrap 5.3 ships no date component of its own.

Below are 16 Bootstrap datepicker examples built from native inputs and small vanilla calendars, followed by a guide to the plugins, their options and common fixes.

By Bogdan Sandu · Updated October 29, 2026

Every example runs on Bootstrap 5.3.8 from the jsDelivr CDN. Add these tags to your page, then paste an example's HTML, CSS and JS.

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.2/font/bootstrap-icons.min.css" rel="stylesheet" integrity="sha384-nWN3KqaaVcbEsCjLSV+2jMjh62Iny/+mN3srM8TZeM8N6aj7eDl5wwYggjAJ4/sJ" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>

Native date, time, month and week inputs

Bootstrap 5.3 has no datepicker, but form-control, input groups and floating labels style every native date and time type. The browser draws the picker itself, so its popup looks different in Chrome, Firefox and Safari, and month and week inputs fall back to plain text in desktop Firefox and Safari.

Cabin booking with linked check-in and check-out

  • type="date"
  • form-floating
  • row g-2
  • min / max
  • card

Two native date inputs with floating labels sit side by side in a row g-2 grid and stack on phones, where a full date needs the whole width. The script sets check-in min to today, keeps check-out at least one night later and caps both a year out, then prints the night count and total. Without JavaScript the fields still work, just without the linked limits.

View code
<div class="card bdp-stay border-0 shadow-sm overflow-hidden">
  <img src="fjord.webp" class="bdp-stay-photo" alt="Deep blue fjord running between grey granite cliffs under a bright sky" width="800" height="600">
  <div class="card-body p-4">
    <div class="d-flex justify-content-between align-items-start gap-3 mb-3">
      <div>
        <h2 class="h5 mb-1">Larchwood Cabin 4</h2>
        <p class="text-body-secondary small mb-0">Sleeps 4 &middot; wood stove &middot; lake view</p>
      </div>
      <p class="mb-0 text-end"><span class="fw-semibold fs-5">&euro;148</span><span class="d-block small text-body-secondary">per night</span></p>
    </div>
    <form class="bdp-stay-form">
      <div class="row g-2 mb-3">
        <div class="col-sm-6 form-floating">
          <input type="date" class="form-control" id="bdp-stay-in" required>
          <label for="bdp-stay-in">Check-in</label>
        </div>
        <div class="col-sm-6 form-floating">
          <input type="date" class="form-control" id="bdp-stay-out" required>
          <label for="bdp-stay-out">Check-out</label>
        </div>
      </div>
      <div class="d-flex justify-content-between align-items-center mb-3">
        <span class="badge rounded-pill text-bg-light border bdp-stay-nights">Pick your dates</span>
        <span class="fw-semibold bdp-stay-total" aria-live="polite"></span>
      </div>
      <div class="d-grid">
        <button type="submit" class="btn btn-dark btn-lg">Reserve</button>
      </div>
    </form>
  </div>
</div>

Webinar scheduler with date, time and datetime-local

  • type="time"
  • type="datetime-local"
  • step
  • row g-3
  • form-text

A date input and a time input with step="900" (15 minutes) and office-hours min and max feed a datetime-local reminder field that the script fills one day earlier. The values are always yyyy-mm-dd, hh:mm and yyyy-mm-ddThh:mm, whatever the reader sees, so the server never parses a locale format.

View code
<div class="card bdp-webinar border-0 shadow-sm">
  <div class="card-body p-4">
    <h2 class="h5 mb-1">Schedule a live session</h2>
    <p class="text-body-secondary small mb-4">Typekit Studio &middot; Variable fonts in production</p>
    <form class="row g-3 bdp-webinar-form">
      <div class="col-sm-6">
        <label for="bdp-web-date" class="form-label">Date</label>
        <input type="date" class="form-control" id="bdp-web-date" required>
      </div>
      <div class="col-sm-6">
        <label for="bdp-web-time" class="form-label">Start time</label>
        <input type="time" class="form-control" id="bdp-web-time" min="08:00" max="20:00" step="900" value="16:30" aria-describedby="bdp-web-time-help" required>
        <div id="bdp-web-time-help" class="form-text">15-minute steps, 08:00 to 20:00</div>
      </div>
      <div class="col-sm-6">
        <label for="bdp-web-length" class="form-label">Length</label>
        <select class="form-select" id="bdp-web-length">
          <option value="30">30 minutes</option>
          <option value="45" selected>45 minutes</option>
          <option value="60">60 minutes</option>
        </select>
      </div>
      <div class="col-sm-6">
        <label for="bdp-web-remind" class="form-label">Email reminder</label>
        <input type="datetime-local" class="form-control" id="bdp-web-remind" step="900">
      </div>
      <div class="col-12">
        <p class="small bg-body-tertiary rounded-3 p-3 mb-0 bdp-webinar-summary" aria-live="polite"></p>
      </div>
      <div class="col-12 d-flex flex-wrap gap-2">
        <button type="submit" class="btn btn-primary bdp-webinar-btn">Schedule session</button>
        <button type="reset" class="btn btn-outline-secondary">Reset</button>
      </div>
    </form>
  </div>
</div>

Date of birth with age limits and live age

  • min / max
  • novalidate
  • was-validated
  • invalid-feedback
  • autocomplete="bday"

Try a date from last year, then a real birthday.

The max attribute is computed so the field only accepts people who are 13 or older, and min stops typos like year 0199. Bootstrap validation styles show the result after submit, autocomplete="bday" lets the browser offer a saved birthday, and the script prints the age so the reader can confirm the year.

View code
<form class="card bdp-bday border-0 shadow-sm bdp-bday-form" novalidate>
  <div class="card-body p-4">
    <h2 class="h5 mb-1">Create your Pinewell profile</h2>
    <p class="text-body-secondary small mb-4">We use your birthday to tailor content and send a treat once a year.</p>
    <label for="bdp-bday-input" class="form-label">Date of birth</label>
    <input type="date" class="form-control form-control-lg" id="bdp-bday-input" autocomplete="bday" aria-describedby="bdp-bday-help" required>
    <div id="bdp-bday-help" class="form-text bdp-bday-help">You must be 13 or older.</div>
    <div class="invalid-feedback">Enter a real date. Pinewell members must be 13 or older.</div>
    <div class="valid-feedback bdp-bday-age"></div>
    <div class="d-grid mt-4">
      <button type="submit" class="btn btn-success btn-lg bdp-bday-btn">Continue</button>
    </div>
  </div>
</form>

Month input for a card expiry date

  • type="month"
  • input-group
  • input-group-text
  • Bootstrap Icons
  • pattern fallback

type="month" returns yyyy-mm, and the script sets min to the current month and max ten years out, then mirrors the value as MM/YY on a sample card. Desktop Firefox and Safari render month inputs as plain text, so a placeholder and a pattern keep that fallback usable. Real payment forms usually use two short text fields from the payment provider instead.

View code
<div class="bdp-pay w-100">
  <div class="bdp-pay-card mb-4" aria-hidden="true">
    <div class="d-flex justify-content-between align-items-start">
      <span class="fw-semibold">Fernway Bank</span>
      <i class="bi bi-wifi bdp-pay-wave"></i>
    </div>
    <div class="bdp-pay-chip"></div>
    <div class="bdp-pay-number">0000 0000 0000 4021</div>
    <div class="d-flex justify-content-between align-items-end">
      <span class="small text-uppercase">Ana Ferreira</span>
      <span class="text-end"><span class="d-block bdp-pay-label">Valid thru</span><span class="fw-semibold bdp-pay-exp">MM/YY</span></span>
    </div>
  </div>
  <form class="bdp-pay-form">
    <label for="bdp-pay-month" class="form-label fw-semibold">Expiry date</label>
    <div class="input-group">
      <span class="input-group-text"><i class="bi bi-calendar2-month" aria-hidden="true"></i></span>
      <input type="month" class="form-control" id="bdp-pay-month" placeholder="2028-04" pattern="[0-9]{4}-[0-9]{2}" aria-describedby="bdp-pay-help" required>
    </div>
    <div id="bdp-pay-help" class="form-text">Sample form. Month and year as printed on the card.</div>
  </form>
</div>

Week picker for a timesheet

  • type="week"
  • table
  • form-control-sm
  • ISO 8601 week
  • table-responsive

A week input returns values like 2026-W44, and the script turns that ISO week into the Monday to Friday dates in the table header. Hours update a running total, and the week defaults to the current one. Where week inputs are unsupported the field becomes text, so the pattern accepts the same yyyy-Www format.

View code
<form class="card bdp-ts border-0 shadow-sm bdp-ts-form">
  <div class="card-body p-4">
    <div class="d-flex flex-wrap justify-content-between align-items-end gap-3 mb-3">
      <div>
        <h2 class="h5 mb-1">Timesheet</h2>
        <p class="text-body-secondary small mb-0">Harbor Lane redesign</p>
      </div>
      <div>
        <label for="bdp-ts-week" class="form-label small fw-semibold mb-1">Week</label>
        <input type="week" class="form-control" id="bdp-ts-week" placeholder="2026-W44" pattern="[0-9]{4}-W[0-9]{2}" required>
      </div>
    </div>
    <div class="table-responsive">
      <table class="table align-middle mb-0 bdp-ts-table">
        <caption class="visually-hidden">Hours per weekday</caption>
        <thead>
          <tr class="bdp-ts-head"></tr>
        </thead>
        <tbody>
          <tr class="bdp-ts-row"></tr>
        </tbody>
      </table>
    </div>
    <p class="d-flex justify-content-between mt-3 mb-0"><span class="text-body-secondary bdp-ts-range"></span><span class="fw-semibold bdp-ts-total" aria-live="polite"></span></p>
  </div>
</form>

Appointment date with time-slot buttons

  • type="date"
  • btn-check
  • radio group
  • fieldset / legend
  • disabled

The date is a native input, and the times are btn-check radios inside a fieldset with a legend, so the slots act as one radio group with arrow-key movement for free. Booked slots get the disabled attribute, and the script reshuffles them per date and prints the choice in a status line.

View code
<form class="card bdp-slot border-0 shadow-sm bdp-slot-form">
  <div class="card-body p-4">
    <h2 class="h5 mb-1">Book a cut at Oak &amp; Fade</h2>
    <p class="text-body-secondary small mb-3">Classic cut &middot; 40 min &middot; &euro;32</p>
    <label for="bdp-slot-date" class="form-label fw-semibold">Day</label>
    <input type="date" class="form-control mb-3" id="bdp-slot-date" required>
    <fieldset>
      <legend class="form-label fw-semibold fs-6">Time</legend>
      <div class="bdp-slot-grid"></div>
    </fieldset>
    <p class="small mt-3 mb-3 bdp-slot-status" role="status"></p>
    <div class="d-grid">
      <button type="submit" class="btn btn-warning bdp-slot-btn">Confirm booking</button>
    </div>
  </div>
</form>

Validation, dark mode, theming and filter bars

Date fields inside real forms: past-date validation with Bootstrap feedback, a dark color mode where color-scheme repaints the native picker, a form re-themed through CSS variables and a report filter with preset ranges.

Delivery date that rejects past days and Sundays

  • novalidate
  • checkValidity()
  • setCustomValidity()
  • invalid-feedback
  • aria-invalid

Submit with an empty field, then try a past date.

min is set to today, so the browser flags past dates as rangeUnderflow, and setCustomValidity() adds a rule min and max cannot express: no Sunday deliveries. The invalid-feedback text changes with the reason, and aria-invalid plus focus on the field make the error reachable without color.

View code
<form class="card bdp-del border-0 shadow-sm bdp-del-form" novalidate>
  <div class="card-body p-4">
    <h2 class="h5 mb-1">When should we deliver?</h2>
    <p class="text-body-secondary small mb-4">Order #48213 &middot; Moss green lounge chair</p>
    <label for="bdp-del-date" class="form-label fw-semibold">Delivery date</label>
    <div class="input-group has-validation">
      <span class="input-group-text"><i class="bi bi-truck" aria-hidden="true"></i></span>
      <input type="date" class="form-control" id="bdp-del-date" aria-describedby="bdp-del-help bdp-del-error" required>
      <div class="invalid-feedback" id="bdp-del-error">Choose a delivery date.</div>
      <div class="valid-feedback">That day works for our drivers.</div>
    </div>
    <div id="bdp-del-help" class="form-text">Monday to Saturday, from today onward.</div>
    <div class="d-grid mt-4">
      <button type="submit" class="btn btn-primary bdp-del-btn">Save delivery date</button>
    </div>
  </div>
</form>

Dark release planner with a dark native picker

  • data-bs-theme="dark"
  • color-scheme: dark
  • form-floating
  • type="datetime-local"
  • badge

Bootstrap 5.3 sets color-scheme: dark on any element with data-bs-theme="dark", which is what turns the calendar icon and the native popup dark in Chromium and Firefox. The extra .bdp-night rule repeats color-scheme for dark panels that do not use Bootstrap color modes, and the accent comes from local button and focus overrides.

View code
<div class="card bdp-night border-0">
  <div class="card-body p-4">
    <div class="d-flex justify-content-between align-items-center gap-2 mb-1">
      <h2 class="h5 mb-0">Release window</h2>
      <span class="badge rounded-pill bdp-night-badge">v4.2.0</span>
    </div>
    <p class="small text-body-secondary mb-4">Northstar CLI &middot; production rollout</p>
    <form class="row g-3 bdp-night-form">
      <div class="col-sm-6">
        <div class="form-floating">
          <input type="datetime-local" class="form-control" id="bdp-night-start" step="1800">
          <label for="bdp-night-start">Freeze starts</label>
        </div>
      </div>
      <div class="col-sm-6">
        <div class="form-floating">
          <input type="datetime-local" class="form-control" id="bdp-night-ship" step="1800">
          <label for="bdp-night-ship">Ship at</label>
        </div>
      </div>
      <div class="col-12">
        <div class="form-floating">
          <input type="date" class="form-control" id="bdp-night-review">
          <label for="bdp-night-review">Post-release review</label>
        </div>
      </div>
      <div class="col-12 d-flex flex-wrap align-items-center justify-content-between gap-2">
        <span class="small text-body-secondary bdp-night-span" aria-live="polite"></span>
        <button type="submit" class="btn bdp-night-btn">Lock window</button>
      </div>
    </form>
  </div>
</div>

Clinic booking form themed with CSS variables

  • --bs-border-radius
  • --bs-body-font-family
  • --bs-btn-*
  • form-floating
  • ::-webkit-calendar-picker-indicator

Scoped overrides of --bs-border-radius, --bs-border-color, --bs-body-bg and the button variables re-skin the whole form without touching Sass. The focus ring is compiled into the form-control rule, so it is overridden on the scoped class, and the calendar icon gets a tint and pointer cursor through ::-webkit-calendar-picker-indicator, which only Chromium and Safari support.

View code
<form class="bdp-clinic w-100 bdp-clinic-form">
  <div class="d-flex align-items-center gap-3 mb-4">
    <span class="bdp-clinic-mark" aria-hidden="true"><i class="bi bi-heart-pulse"></i></span>
    <div>
      <h2 class="h5 mb-0">Riverside Family Clinic</h2>
      <p class="small text-body-secondary mb-0">Book a check-up with Dr. Imani Hale</p>
    </div>
  </div>
  <div class="row g-3">
    <div class="col-sm-7">
      <div class="form-floating">
        <input type="date" class="form-control" id="bdp-clinic-date" required>
        <label for="bdp-clinic-date">Preferred day</label>
      </div>
    </div>
    <div class="col-sm-5">
      <div class="form-floating">
        <input type="time" class="form-control" id="bdp-clinic-time" min="08:30" max="17:00" step="1800" value="10:30" required>
        <label for="bdp-clinic-time">Time</label>
      </div>
    </div>
    <div class="col-12">
      <div class="form-floating">
        <select class="form-select" id="bdp-clinic-reason">
          <option>Annual check-up</option>
          <option>Vaccination</option>
          <option>Follow-up visit</option>
        </select>
        <label for="bdp-clinic-reason">Reason for visit</label>
      </div>
    </div>
    <div class="col-12 d-grid">
      <button type="submit" class="btn btn-lg bdp-clinic-btn">Request appointment</button>
    </div>
  </div>
</form>

Report filter bar with preset ranges

  • btn-check
  • type="date"
  • input-group-sm
  • flex-wrap
  • aria-live

Preset ranges are btn-check radios that fill the From and To date inputs, and typing a custom date switches the selection to Custom. The To field min follows From, so the browser blocks inverted ranges. The bar wraps with flex-wrap instead of a btn-group, so it never overflows a phone screen.

View code
<section class="bdp-fb border-bottom">
  <div class="d-flex flex-wrap align-items-center justify-content-between gap-3 mb-3">
    <h2 class="h5 mb-0">Orders overview</h2>
    <p class="small text-body-secondary mb-0 bdp-fb-status" aria-live="polite"></p>
  </div>
  <form class="d-flex flex-wrap align-items-end gap-3 bdp-fb-form">
    <fieldset class="d-flex flex-wrap gap-2">
      <legend class="visually-hidden">Date range preset</legend>
      <input type="radio" class="btn-check" name="bdp-fb-preset" id="bdp-fb-today" value="0">
      <label class="btn btn-sm btn-outline-secondary" for="bdp-fb-today">Today</label>
      <input type="radio" class="btn-check" name="bdp-fb-preset" id="bdp-fb-7" value="6" checked>
      <label class="btn btn-sm btn-outline-secondary" for="bdp-fb-7">Last 7 days</label>
      <input type="radio" class="btn-check" name="bdp-fb-preset" id="bdp-fb-30" value="29">
      <label class="btn btn-sm btn-outline-secondary" for="bdp-fb-30">Last 30 days</label>
      <input type="radio" class="btn-check" name="bdp-fb-preset" id="bdp-fb-month" value="month">
      <label class="btn btn-sm btn-outline-secondary" for="bdp-fb-month">This month</label>
      <input type="radio" class="btn-check" name="bdp-fb-preset" id="bdp-fb-custom" value="custom">
      <label class="btn btn-sm btn-outline-secondary" for="bdp-fb-custom">Custom</label>
    </fieldset>
    <div class="d-flex flex-wrap gap-2">
      <div class="input-group input-group-sm bdp-fb-field">
        <label class="input-group-text" for="bdp-fb-from">From</label>
        <input type="date" class="form-control" id="bdp-fb-from">
      </div>
      <div class="input-group input-group-sm bdp-fb-field">
        <label class="input-group-text" for="bdp-fb-to">To</label>
        <input type="date" class="form-control" id="bdp-fb-to">
      </div>
    </div>
  </form>
</section>

Calendar widgets built from Bootstrap pieces

When the native picker cannot show availability or a range, a small vanilla script can build the calendar from a dropdown, a modal, a table and buttons. Each one uses a grid with roving focus, arrow keys, Enter and Escape, aria-selected and aria-current="date", and builds its DOM with createElement and textContent only.

Calendar dropdown attached to a text input

  • dropdown
  • data-bs-auto-close="outside"
  • input-group
  • role="grid"
  • aria-current="date"

Open the calendar, then use the arrow keys and Enter.

A Bootstrap dropdown with data-bs-auto-close="outside" holds a table with role="grid", so clicks on the month arrows keep it open. The script stops arrow keys from reaching the dropdown keyboard handler, moves a roving tabindex across day buttons, and lets Escape fall through so Bootstrap closes the menu and returns focus to the button. The text field still accepts typed yyyy-mm-dd dates.

View code
<form class="bdp-dd w-100 bdp-dd-form">
  <label for="bdp-dd-input" class="form-label fw-semibold">Start date</label>
  <div class="input-group">
    <input type="text" class="form-control" id="bdp-dd-input" placeholder="yyyy-mm-dd" inputmode="numeric" autocomplete="off" aria-describedby="bdp-dd-help">
    <div class="dropdown">
      <button type="button" class="btn btn-outline-secondary dropdown-toggle bdp-dd-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false">
        <i class="bi bi-calendar3" aria-hidden="true"></i><span class="visually-hidden bdp-dd-toggle-label">Choose start date</span>
      </button>
      <div class="dropdown-menu dropdown-menu-end p-3 bdp-cal" role="dialog" aria-modal="false" aria-labelledby="bdp-dd-title">
        <div class="d-flex justify-content-between align-items-center mb-2">
          <button type="button" class="btn btn-sm btn-light bdp-cal-prev" aria-label="Previous month"><i class="bi bi-chevron-left" aria-hidden="true"></i></button>
          <h3 class="h6 mb-0 bdp-cal-title" id="bdp-dd-title" aria-live="polite"></h3>
          <button type="button" class="btn btn-sm btn-light bdp-cal-next" aria-label="Next month"><i class="bi bi-chevron-right" aria-hidden="true"></i></button>
        </div>
        <table class="bdp-cal-grid" role="grid" aria-labelledby="bdp-dd-title">
          <thead>
            <tr><th scope="col" abbr="Monday">Mo</th><th scope="col" abbr="Tuesday">Tu</th><th scope="col" abbr="Wednesday">We</th><th scope="col" abbr="Thursday">Th</th><th scope="col" abbr="Friday">Fr</th><th scope="col" abbr="Saturday">Sa</th><th scope="col" abbr="Sunday">Su</th></tr>
          </thead>
          <tbody class="bdp-cal-body"></tbody>
        </table>
      </div>
    </div>
  </div>
  <div id="bdp-dd-help" class="form-text">Type yyyy-mm-dd or open the calendar.</div>
</form>

Inline availability calendar with booked days

  • card
  • role="grid"
  • aria-disabled
  • aria-selected
  • role="status"

Use the mouse, or Tab into the calendar and move with the arrow keys, Page Up and Page Down.

An always-open calendar for a tour operator: past days, Mondays and sold-out dates get aria-disabled="true" and a struck style but stay focusable, so arrow keys never skip silently and screen readers still announce them as unavailable. A chosen day sets aria-selected on its cell and fills a status line and the booking button.

View code
<div class="card bdp-av border-0 shadow-sm">
  <div class="card-body p-4">
    <h2 class="h5 mb-1">Sea kayak tour, half day</h2>
    <p class="small text-body-secondary mb-3">Saltmarsh Outfitters &middot; closed on Mondays</p>
    <div class="d-flex justify-content-between align-items-center mb-2">
      <button type="button" class="btn btn-sm btn-outline-secondary bdp-cal-prev" aria-label="Previous month"><i class="bi bi-chevron-left" aria-hidden="true"></i></button>
      <h3 class="h6 mb-0" id="bdp-av-title" aria-live="polite"></h3>
      <button type="button" class="btn btn-sm btn-outline-secondary bdp-cal-next" aria-label="Next month"><i class="bi bi-chevron-right" aria-hidden="true"></i></button>
    </div>
    <table class="bdp-cal-grid" role="grid" aria-labelledby="bdp-av-title">
      <thead>
        <tr><th scope="col" abbr="Monday">Mo</th><th scope="col" abbr="Tuesday">Tu</th><th scope="col" abbr="Wednesday">We</th><th scope="col" abbr="Thursday">Th</th><th scope="col" abbr="Friday">Fr</th><th scope="col" abbr="Saturday">Sa</th><th scope="col" abbr="Sunday">Su</th></tr>
      </thead>
      <tbody class="bdp-cal-body"></tbody>
    </table>
    <div class="d-flex flex-wrap gap-3 small text-body-secondary mt-2 mb-3">
      <span><span class="bdp-av-key bdp-av-key-open" aria-hidden="true"></span> Available</span>
      <span><span class="bdp-av-key bdp-av-key-full" aria-hidden="true"></span> Sold out or closed</span>
    </div>
    <p class="small mb-3 bdp-av-status" role="status">Pick a day to see open seats.</p>
    <div class="d-grid">
      <button type="button" class="btn btn-primary bdp-av-book" disabled>Book seats</button>
    </div>
  </div>
</div>

Hotel range picker with an inline calendar

  • row / col-md-*
  • role="grid"
  • aria-selected
  • list-group
  • CSS variables

Click a check-in day, then a check-out day.

The first click sets check-in and the second sets check-out, and every night in between gets aria-selected="true" on its cell and a soft band, so the range is exposed to assistive technology as well as drawn. Past days are aria-disabled. The summary is a plain list-group, and the grid stacks under it below the md breakpoint.

View code
<div class="card bdp-hr border-0 shadow-sm overflow-hidden">
  <div class="row g-0">
    <div class="col-md-7 p-4">
      <div class="d-flex justify-content-between align-items-center mb-2">
        <button type="button" class="btn btn-sm btn-light bdp-cal-prev" aria-label="Previous month"><i class="bi bi-chevron-left" aria-hidden="true"></i></button>
        <h2 class="h6 mb-0" id="bdp-hr-title" aria-live="polite"></h2>
        <button type="button" class="btn btn-sm btn-light bdp-cal-next" aria-label="Next month"><i class="bi bi-chevron-right" aria-hidden="true"></i></button>
      </div>
      <table class="bdp-cal-grid" role="grid" aria-labelledby="bdp-hr-title" aria-multiselectable="true">
        <thead>
          <tr><th scope="col" abbr="Monday">Mo</th><th scope="col" abbr="Tuesday">Tu</th><th scope="col" abbr="Wednesday">We</th><th scope="col" abbr="Thursday">Th</th><th scope="col" abbr="Friday">Fr</th><th scope="col" abbr="Saturday">Sa</th><th scope="col" abbr="Sunday">Su</th></tr>
        </thead>
        <tbody class="bdp-cal-body"></tbody>
      </table>
    </div>
    <div class="col-md-5 bdp-hr-side p-4 d-flex flex-column">
      <img src="canal-houses.webp" class="bdp-hr-photo mb-3" alt="Old brick houses and a white mansion along a calm canal with small boats moored by a wooden terrace" width="800" height="600">
      <h3 class="h6 mb-3">Canal House Suites</h3>
      <ul class="list-group list-group-flush small mb-3">
        <li class="list-group-item d-flex justify-content-between px-0"><span>Check-in</span><span class="fw-semibold bdp-hr-in">Select</span></li>
        <li class="list-group-item d-flex justify-content-between px-0"><span>Check-out</span><span class="fw-semibold bdp-hr-out">Select</span></li>
        <li class="list-group-item d-flex justify-content-between px-0"><span class="bdp-hr-nights">0 nights</span><span class="fw-semibold bdp-hr-total">&euro;0</span></li>
      </ul>
      <p class="visually-hidden bdp-hr-live" role="status"></p>
      <button type="button" class="btn btn-dark mt-auto bdp-hr-book" disabled>Reserve</button>
    </div>
  </div>
</div>

Date picker dialog in a Bootstrap modal

  • modal
  • modal-sm
  • aria-labelledby
  • role="grid"
  • shown.bs.modal

Open the dialog, move with the arrow keys, press Enter, then Use date.

This follows the W3C APG date picker dialog shape with Bootstrap parts: a labeled text input, a button that opens a modal, and a grid inside it. The modal supplies the focus trap, Escape and aria-modal, the script focuses the selected day on shown.bs.modal, and Enter on a day picks it while the Use date button writes it back and updates the opener label.

View code
<form class="bdp-mp w-100 bdp-mp-form">
  <label for="bdp-mp-input" class="form-label fw-semibold">Interview date</label>
  <div class="input-group">
    <input type="text" class="form-control" id="bdp-mp-input" placeholder="yyyy-mm-dd" autocomplete="off" aria-describedby="bdp-mp-help">
    <button type="button" class="btn btn-outline-dark bdp-mp-open" data-bs-toggle="modal" data-bs-target="#bdp-mp-modal" aria-label="Choose interview date"><i class="bi bi-calendar-event" aria-hidden="true"></i></button>
  </div>
  <div id="bdp-mp-help" class="form-text">Format yyyy-mm-dd. Interviews run Tuesday to Thursday.</div>
</form>
<div class="modal fade bdp-mp-modal" id="bdp-mp-modal" tabindex="-1" aria-labelledby="bdp-mp-heading" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered bdp-mp-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h2 class="modal-title fs-6" id="bdp-mp-heading">Choose interview date</h2>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        <div class="d-flex justify-content-between align-items-center mb-2">
          <button type="button" class="btn btn-sm btn-light bdp-cal-prev" aria-label="Previous month"><i class="bi bi-chevron-left" aria-hidden="true"></i></button>
          <h3 class="h6 mb-0" id="bdp-mp-title" aria-live="polite"></h3>
          <button type="button" class="btn btn-sm btn-light bdp-cal-next" aria-label="Next month"><i class="bi bi-chevron-right" aria-hidden="true"></i></button>
        </div>
        <table class="bdp-cal-grid" role="grid" aria-labelledby="bdp-mp-title">
          <thead>
            <tr><th scope="col" abbr="Monday">Mo</th><th scope="col" abbr="Tuesday">Tu</th><th scope="col" abbr="Wednesday">We</th><th scope="col" abbr="Thursday">Th</th><th scope="col" abbr="Friday">Fr</th><th scope="col" abbr="Saturday">Sa</th><th scope="col" abbr="Sunday">Su</th></tr>
          </thead>
          <tbody class="bdp-cal-body"></tbody>
        </table>
        <p class="small text-body-secondary mt-2 mb-0 bdp-mp-picked" aria-live="polite"></p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
        <button type="button" class="btn btn-dark bdp-mp-ok">Use date</button>
      </div>
    </div>
  </div>
</div>

Month picker grid for billing periods

  • btn
  • role="grid"
  • aria-selected
  • aria-disabled
  • btn-group

Use the mouse, or Tab into the calendar and move with the arrow keys, Page Up and Page Down.

A twelve-month grid works in every browser, unlike type="month", which desktop Firefox and Safari show as text. Months are buttons in a 3 by 4 grid with roving focus: Left and Right move one month, Up and Down move four, and future months are aria-disabled. The year switcher is a small btn-group.

View code
<div class="card bdp-mg border-0 shadow-sm">
  <div class="card-body p-4">
    <div class="d-flex justify-content-between align-items-center gap-2 mb-3">
      <h2 class="h6 mb-0" id="bdp-mg-label">Billing period</h2>
      <div class="btn-group btn-group-sm" role="group" aria-label="Change year">
        <button type="button" class="btn btn-outline-secondary bdp-mg-prev" aria-label="Previous year"><i class="bi bi-chevron-left" aria-hidden="true"></i></button>
        <span class="btn btn-outline-secondary disabled bdp-mg-year" aria-live="polite"></span>
        <button type="button" class="btn btn-outline-secondary bdp-mg-next" aria-label="Next year"><i class="bi bi-chevron-right" aria-hidden="true"></i></button>
      </div>
    </div>
    <table class="bdp-mg-grid" role="grid" aria-labelledby="bdp-mg-label">
      <tbody class="bdp-mg-body"></tbody>
    </table>
    <div class="d-flex justify-content-between align-items-center mt-3 pt-3 border-top">
      <span class="small text-body-secondary bdp-mg-status" role="status"></span>
      <span class="fw-semibold bdp-mg-amount"></span>
    </div>
  </div>
</div>

Week strip for picking a delivery day

  • role="radiogroup"
  • aria-checked
  • aria-current="date"
  • btn
  • d-grid

Use the mouse, or Tab into the calendar and move with the arrow keys, Page Up and Page Down.

A horizontal strip of seven days is what food delivery and fitness apps use instead of a month grid. It follows the radio group pattern: one tab stop, Left and Right move and select, Home and End jump to the ends, and the arrow buttons page by week. Today carries aria-current="date" and days without slots are aria-disabled.

View code
<div class="card bdp-ws border-0 shadow-sm">
  <div class="card-body p-4">
    <div class="d-flex justify-content-between align-items-center mb-3">
      <h2 class="h6 mb-0" id="bdp-ws-label">Delivery day</h2>
      <div class="d-flex gap-1">
        <button type="button" class="btn btn-sm btn-light bdp-ws-prev" aria-label="Previous week"><i class="bi bi-arrow-left" aria-hidden="true"></i></button>
        <button type="button" class="btn btn-sm btn-light bdp-ws-next" aria-label="Next week"><i class="bi bi-arrow-right" aria-hidden="true"></i></button>
      </div>
    </div>
    <div class="bdp-ws-days" role="radiogroup" aria-labelledby="bdp-ws-label"></div>
    <div class="bdp-ws-panel mt-3">
      <p class="fw-semibold mb-1 bdp-ws-day" aria-live="polite"></p>
      <p class="small text-body-secondary mb-0 bdp-ws-slots"></p>
    </div>
  </div>
</div>

What does Bootstrap datepicker refer to?

It depends on who is writing. Sometimes it means bootstrap-datepicker, the jQuery plugin the uxsolutions organization maintains on GitHub. Other times it means any picker styled to fit Bootstrap, whatever engine runs underneath.

The job is the same either way. A calendar opens from a text input, and the chosen date goes back into the field in a set format.

Pages that rank for the term mix both meanings, so two tutorials with the same title can ask for completely different scripts. The documentation pages that borrow the name don’t agree on what it is either.

  • Older jQuery-era themes mean the uxsolutions plugin, started with $(‘.datepicker’).datepicker()
  • Themesberg Volt uses Vanilla JS Datepicker instead, started with new Datepicker(el, { buttonClass: ‘btn’ })
  • Argon Dashboard from Creative Tim runs Flatpickr, loaded from flatpickr.min.js
  • CoreUI and MDB ship their own components, driven by data-coreui-toggle=”date-picker” and the Datetimepicker class in mdb-ui-kit

Copy a snippet without checking which engine it targets and the first error you hit is a missing function.

The Bootstrap framework itself ships no date component. Its 5.3 component list runs from accordion to tooltips without one.

The closest built-in option is a native date input styled with .form-control. The browser draws the calendar, so format, language and week start stay under the browser’s control.

Which Bootstrap datepicker should you use with Bootstrap 5?

On a new Bootstrap 5 build I’d go with a jQuery-free picker. Tempus Dominus covers date and time, and Vanilla JS Datepicker covers date only. The uxsolutions plugin earns its place on pages where jQuery already runs.

According to the official migration notes, Bootstrap 5.0 dropped jQuery and moved from Popper 1 to Popper 2. The uxsolutions plugin stayed on jQuery. Its package lists jQuery as a dependency, and the range in 1.10.1 runs from 3.4.0 up to, but not including, 4.0.0.

PickerjQueryBootstrap stylingInstallLicense
bootstrap-datepicker 1.10.1Required (npm dependency, jQuery 3.4 to 3.x)Stylesheets for Bootstrap 2 and 3, plus standalone buildsnpm, cdnjs, jsDelivrApache-2.0
Tempus Dominus 6.10.4Not required (optional jQuery provider)Bootstrap not required, Popper 2 requirednpm, jsDelivr, NuGetMIT
Vanilla JS Datepicker 1.3.4Not requiredReady-made CSS and Sass files for Bootstrap 5 and Bootstrap 4npm, jsDelivrMIT
Flatpickr 4.6.13Not requiredOwn themes, not tied to Bootstrapnpm, cdnjsMIT

Maintenance status of bootstrap-datepicker

The uxsolutions repository on GitHub shows 789 open issues and 96 open pull requests (October 2026). Version 1.10.1, published in September 2025, is still the Latest release there and on npm. That backlog doesn’t settle much on its own. Flatpickr sits at 725 open issues and 135 open pull requests on the same date, and its latest npm release, 4.6.13, dates from April 2022. Vanilla JS Datepicker’s latest, 1.3.4, is from August 2023.

The release notes tell you more. The 1.10.1 notes include a pull request for the jQuery advisory CVE-2019-11358 (jQuery below 3.4.0), so load a current 3.x build instead of whatever your theme bundles.

Tempus Dominus has its own catch. Its README says the author is moving on to other projects and that new issues need to be sponsored, which counts against it if you expect free support. Its latest release, 6.10.4, came out in May 2025.

Choosing for Bootstrap 5

jQuery still runs on 64.4% of all websites, and on 85.5% of those with a known JavaScript library (W3Techs, 28 October 2026). So the jQuery plugin remains a fair pick when the library is already loaded, and with Bootstrap 3 or 4 on the page it’s the obvious one.

A new Bootstrap 5 page that needs date and time should get Tempus Dominus 6.10.4. Load Popper first and bring an icon font, since it defaults to Font Awesome 6.

For date only, Vanilla JS Datepicker 1.3.4 is a from-scratch remake of bootstrap-datepicker with similar usability and no dependencies. If the look doesn’t have to match Bootstrap at all, Flatpickr does the job.

I wouldn’t add jQuery to a page just for a date field, and size doesn’t change that. The plugin’s minified file is about 33.9 kB in the 1.10.1 package listing on jsDelivr, with jQuery loading on top of it. Vanilla JS Datepicker’s README gives 35 kB minified and 11 kB gzipped, and nothing else to load.

The older eonasdan/bootstrap-datetimepicker 4.17.49 is one to skip. It still requires jQuery 1.9.1 or newer and Moment.js 2.10.5 or newer, per Packagist, and Tempus Dominus replaced it.

How to add Bootstrap datepicker to an input field

Setup for the uxsolutions plugin follows the usual jQuery plugin order. Stylesheet first, then jQuery, then the plugin script, and after that a datepicker() call on an input.

  1. Add the plugin stylesheet to the head.
  2. Load jQuery 3.x before any script that depends on it. The package’s dependency range stops below jQuery 4 (4.0.0 shipped in January 2026), so use the latest 3.x release, 3.7.1.
  3. Load the plugin script after jQuery.
  4. Add a text input with the form-control class and initialize it.

Install the files

With npm it’s npm install bootstrap-datepicker, and jQuery gets installed separately. cdnjs is hosted by Cloudflare, though the README warns that a new version can take a few hours to appear there.

jsDelivr serves the npm package, so its version tags match npm. The manual route is copying the dist folder from the GitHub repository.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-datepicker@1.10.1/dist/css/bootstrap-datepicker.standalone.min.css">
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-datepicker@1.10.1/dist/js/bootstrap-datepicker.min.js"></script>

In dist/css, the files with no suffix target Bootstrap 2 and the ones with a 3 suffix target Bootstrap 3. Standalone variants of both drop the dependency on Bootstrap’s own CSS.

Nothing is labeled for Bootstrap 4 or 5. On those versions, start with the standalone build and check the popup against your theme.

Add the input markup

The simplest markup is a text input with the .form-control class. Focusing it by click or tab opens the picker.

Build the field with standard Bootstrap form markup and give it an id, so a label can point at it later.

The plugin’s docs show an icon variant built on .input-group-addon, which is Bootstrap 3 markup. Bootstrap 5 dropped .input-group-append and .input-group-prepend, so .input-group-text now sits directly inside the group.

Use a label as the icon wrapper, with a calendar glyph from Bootstrap Icons inside it. A click on the label focuses the input, and focus opens the picker.

<div class="input-group">
  <input type="text" class="form-control" id="start-date" placeholder="dd/mm/yyyy">
  <label class="input-group-text" for="start-date"><i class="bi bi-calendar3"></i></label>
</div>

Initialize the picker

You can skip JavaScript entirely by adding data-provide=”datepicker” to the input and setting options through data-date-* attributes.

<input type="text" class="form-control" data-provide="datepicker" data-date-autoclose="true" data-date-format="dd/mm/yyyy">

The other way is to select the input and call datepicker() with an options object. I prefer this one, since the options sit next to the rest of the page logic.

$('#start-date').datepicker({
  autoclose: true,
  format: 'dd/mm/yyyy'
});

The data API can be switched off with $(document).off(‘.datepicker.data-api’), the same way as other Bootstrap plugins, per the docs.

Initializing on a plain div gives an inline picker that stays open. Arrow-key navigation does not work in that mode, according to the options documentation.

Bootstrap datepicker options and their defaults

Options go into an object passed to datepicker(), or into data attributes. Every one has a default, so an unconfigured picker works. The docs list more than 40, and most pages only touch a handful.

The data attribute for an option is its name in lowercase dashes, prefixed with data-date-. So startDate becomes data-date-start-date, and daysOfWeekDisabled becomes data-date-days-of-week-disabled.

The defaults you’ll run into first:

  • format is mm/dd/yyyy
  • autoclose is false, so the popup stays open after a pick
  • weekStart is 0, which is Sunday
  • todayBtn is false (true only moves the view to today, “linked” also selects it)
  • clearBtn is false
  • startView is 0, the days view of the current month

To change a default for every picker on the page, assign it on $.fn.datepicker.defaults before initializing.

One default is disputed inside the official docs. The todayHighlight entry says false, while the quick-reference table on the same page says true.

The 1.10.1 release notes list a pull request about a wrong todayHighlight row in the defaults table, and the docs source on GitHub now says false, yet the readthedocs page, checked in October 2026, still shows true. Set todayHighlight explicitly and the question never comes up.

Setting the date format and language

The format option controls the string written into the input. Month names, weekday names and the week start come from the language option.

Format tokens and ISO dates

A format string combines the tokens below, and the default is mm/dd/yyyy.

TokenOutputExample
d, ddDay of month, without and with leading zero5, 05
D, DDWeekday, abbreviated and fullMon, Monday
m, mmMonth number, without and with leading zero7, 07
M, MMMonth name, abbreviated and fullJan, January
yy, yyyyYear, 2 and 4 digits12, 2012

For server-side parsing, yyyy-mm-dd matches the ISO 8601 date format and sorts correctly as plain text.

When tokens fall short, format accepts an object with toDisplay and toValue functions. The docs example converts in both directions: its toDisplay shifts the date back seven days and returns an ISO string, and its toValue shifts it forward again. A code comment there mentions storing UTC while the interface shows local dates.

Parsing has a couple of traps. forceParse (default true) rewrites an invalid typed date into a valid one when the picker closes. And two-digit years are read literally unless assumeNearbyYear is on, so 5/1/15 means year 15, not 2015.

Language and locale files

Locale files sit in dist/locales (bootstrap-datepicker.de.min.js in 1.10.1) and load after the main plugin script. A full code such as de-DE falls back to de, and an unknown code falls back to English.

<script src="https://cdn.jsdelivr.net/npm/bootstrap-datepicker@1.10.1/dist/locales/bootstrap-datepicker.de.min.js" charset="UTF-8"></script>
$('#start-date').datepicker({
  language: 'de',
  format: 'dd.mm.yyyy'
});

If characters render wrong, the browser is reading the locale file with a non-Unicode encoding. The docs fix it with charset=”UTF-8″ on the script tag, as shown above.

Month and weekday names also land in the input value and go to the server on submit. A format with MM under a German locale sends German month names.

Locale files carry their own default format, and the changelogs record per-locale format fixes (Romanian uses dd/mm/yyyy). Set format explicitly so the output never depends on which locale file loaded.

A missing language can be added through $.fn.datepicker.dates before calling datepicker(), with day names, month names, today, clear, format and weekStart. Right-to-left languages add rtl: true.

Restricting dates and building a date range

startDate and endDate set the selectable window. daysOfWeekDisabled and datesDisabled remove single days, and beforeShowDay handles anything rule-based. A range uses two inputs inside an input-daterange container, which links their pickers.

Setting the selectable window

startDate and endDate accept a Date object, a string that parses with the active format, or a relative value such as “-1d” or “+6m +1y”. Valid units are d, w, m and y, and “0” means today.

The aliases yesterday, today and tomorrow save typing. Because strings must parse with the active format, changing format means rewriting any absolute date strings.

$('#arrival').datepicker({
  startDate: '+1d',
  endDate: '+6m'
});

That opens tomorrow through six months out, a common booking window.

Disabling weekends and specific dates

For weekends, daysOfWeekDisabled takes 0 for Sunday and 6 for Saturday, so ‘0,6’ or [0,6] blocks both. datesDisabled wants one date string or an array of them, written in the active format.

beforeShowDay is a function that receives each date and returns a Boolean, a CSS class string, or an object with enabled, classes, tooltip and content.

$('#arrival').datepicker({
  format: 'dd/mm/yyyy',
  daysOfWeekDisabled: [0, 6],
  datesDisabled: ['24/12/2026', '25/12/2026'],
  beforeShowDay: function (date) {
    if (date.getDate() === 1) {
      return { enabled: false, tooltip: 'Closed on the 1st' };
    }
  }
});

Returning nothing leaves a date untouched, so the function above only affects the first of each month.

Since 1.7.0, the data-date-dates-disabled attribute accepts a comma-separated list. Older copies need the JavaScript form for more than one date.

Linking two inputs as a range

Put two inputs inside a container with the input-daterange class, then call datepicker() on the container. Each input gets its own picker, and the markup docs say the pickers are linked so a range can be selected.

<div class="input-group input-daterange">
  <input type="text" class="form-control" placeholder="Check-in">
  <span class="input-group-text">to</span>
  <input type="text" class="form-control" placeholder="Check-out">
</div>
$('.input-daterange').datepicker({
  format: 'dd/mm/yyyy',
  autoclose: true
});

Methods run on the inputs rather than the container, so clearing a range looks like $(‘.input-daterange input’).datepicker(‘clearDates’).

keepEmptyValues is false by default, which means picking one date fills the empty partner with the same value. A container that isn’t an input-daterange needs the two inputs passed through the inputs option.

Preset ranges such as last 7 days sit outside this plugin. Dan Grossman’s daterangepicker offers predefined ranges, a time picker and a single date mode, and its repository carries a Moment.js file.

Check its age before adopting it. The GitHub page shows a license notice dated 2012-2020 and no Issues tab, and the latest npm release, 3.1.0, is from May 2020.

Bootstrap datepicker events and methods

Events tell your code when the date or the calendar view changes, and methods let your code read, set or clear the date. Events attach with .on(‘changeDate’, fn), and methods run through .datepicker(‘methodName’, args).

Reacting to a date change

The Events page lists eight events, and 1.7.0 added a ninth, changeViewMode, that the page does not list.

changeDate fires when the date changes, and clearDate fires when it’s cleared, usually through the clear button. show and hide fire as the popup opens and closes. The view-movers are changeMonth, changeYear, changeDecade and changeCentury, which fire when the calendar moves but the date stays put.

Every handler receives an event object with date, dates and a format() helper. On a multidate picker, date is the latest pick and dates holds all of them.

$('#start-date')
.on('changeDate', function (e) {
  $('#start-iso').val(e.format('yyyy-mm-dd'));
})
.on('clearDate', function () {
  $('#start-iso').val('');
});

Bind both events. Pressing Enter on the already selected date deselects it and fires clearDate instead of changeDate, so a handler on changeDate alone keeps a stale value.

Passing the value on change usually means an Ajax request. Send the string from e.format(), not a Date object, so no timezone shift reaches the server.

Since 1.7.0, initialization no longer fires changeDate. Set any starting value in your own code.

Reading and setting dates from code

TaskCallNote
Read the date as a local Datedatepicker(‘getDate’)Reads the first picker in the selection, and the latest date on a multidate picker
Read the raw UTC datedatepicker(‘getUTCDate’)Returned as stored, with no conversion to local time
Set the datedatepicker(‘setDate’, new Date(2026, 9, 22))Local Date, converted to UTC internally
Clear the datedatepicker(‘clearDates’)Takes no arguments
Move the limits latersetStartDate, setEndDateOmit the argument to remove the limit

setDate treats its argument as local, while getUTCDate hands back the internal object untouched. Mix the two and the result can differ by a day, depending on the browser’s offset.

The docs have a gap here. The Markup page’s hidden-field example calls getFormattedDate, but the Methods page has no entry for it. Inside an event handler, e.format() is the documented route to a formatted string.

Methods are chainable since 1.4.0. destroy strips the events, internal objects and added HTML, and remove stays as its alias after the 1.6.0 rename.

Why is Bootstrap datepicker not working?

Scripts loaded in the wrong order, or twice, cause most of it. The rest comes from another library claiming the .datepicker class, the wrong plugin name, CSS and JS from different versions, or a modal that buries or detaches the popup.

SymptomCauseFix
$(…).datepicker is not a functionPlugin file missing, loaded before jQuery, or a second jQuery load replaced the firstLoad jQuery once, then the plugin, and check the Network tab for 404s
Another calendar appears, or options are ignoredA second library registers datepicker too, such as jQuery UI Datepicker or a UI kitCall $.fn.datepicker.noConflict() and use the alias
$(…).datetimepicker is not a functionThe call uses the wrong plugin nameCall datepicker, or install the separate datetimepicker plugin if time is needed
Popup unstyled or misplacedCSS and JS from different versions, or a Bootstrap 3 stylesheet on a newer BootstrapMatch the version numbers and try the standalone stylesheet
Popup behind, detached from or scrolling away from a modalStacking and container rulesSet the container option to the modal element

The noConflict call comes from the plugin’s own docs. It returns datepicker to whatever was assigned before, so load the other library first and run it right after the plugin.

var datepicker = $.fn.datepicker.noConflict();
$.fn.bootstrapDP = datepicker;
$('#start-date').bootstrapDP({ autoclose: true });

Inside a Bootstrap modal

A Bootstrap modal uses position: fixed, and the official 5.3 docs advise keeping modal markup at the top level of the page. By default the picker’s popup is appended to body, outside the modal.

If the popup lands behind the modal, check your version first. Versions before 1.3.1 stopped the z-index calculation at the first ancestor, so updating usually fixes it.

Drifting on scroll has a different cause. A popup appended to body is positioned against the page, so it can stay put while the modal scrolls. The container option, added in 1.4.0, fixes that.

Still sitting too low? The popup z-index is the highest value among the input and its ancestors plus zIndexOffset, which defaults to 10. Raise the offset when a custom stacking context sits above.

$('#start-date').datepicker({
  container: '#bookingModal',
  autoclose: true
});

Append to the .modal element itself, not .modal-body. A scrollable modal body clips whatever overflows it.

Pickers that submit the form

A button inside a form submits it by default, so a calendar-icon button needs type=”button”. Pressing Enter while the popup is closed submits the form as usual, per the keyboard docs. While the popup is open, Enter toggles the focused date instead.

None of this is a plugin bug. It’s standard form behavior that shows up once a picker adds buttons and key handling.

When a datepicker plugin is the wrong choice

Skip the plugin when the form needs one date from a user who already knows it. A native date input does that job without a script.

Native date input versus the plugin

MDN rates the date input Baseline widely available (newly available since April 2021, widely since October 2023), and its value always arrives as yyyy-mm-dd, whatever the display locale.

Can I Use puts global support at 90.36% full plus 6.88% partial, 97.24% in total (data updated 22 October 2026).

The two sources read differently because they score different things. Can I Use rates date, time, datetime-local, month and week as one feature, which is the likely reason desktop Firefox and Safari show as partial.

NeedNative date inputbootstrap-datepicker
Fixed display such as dd.mm.yyyyDisplay follows the browser locale, value stays yyyy-mm-ddformat option
Block weekends or listed datesmin, max and step onlydaysOfWeekDisabled, datesDisabled, beforeShowDay
Linked start and end datesTwo inputs and your own scriptinput-daterange container
One known date, no special rulesFits as isAdds jQuery plus CSS and JS files

step counts in days, so with min set to a Saturday, step=”7″ makes only Saturdays selectable, but nothing more irregular than that.

Accessibility gaps in the plugin’s popup

Treat the popup as untested for assistive technology until someone has tested it.

The W3C ARIA Authoring Practices Guide shows a date picker dialog: a text input, a button that opens a modal dialog, and a calendar built as a grid. The page, last updated 12 August 2025, says its code is not meant for production and needs testing with assistive technologies.

That example ties a format hint to the input with aria-describedby and gives the dialog a role with aria-modal and a label. The grid supports arrow keys, Home, End, Page Up and Page Down, with Shift for year changes, and the button name changes to include the chosen date.

The bootstrap-datepicker docs describe a lot less.

  • Arrow keys move by day and week, Shift by month, Ctrl by year
  • Enter toggles the focused date, and Escape clears the focused date and hides and re-shows the popup, which the docs say is necessary before editing the value by hand
  • No Home, End or Page keys are documented
  • The keyboard, markup and options pages mention no ARIA roles or attributes

Whether the popup exposes equivalent ARIA semantics is not documented, so test it with a screen reader before shipping.

Whatever the picker, keep a labeled text field that accepts typing, with a visible format hint. That does more for accessible forms than the calendar does, and it is the part of the APG example that needs no script.

Bootstrap datepicker FAQ

How do you select more than one date?

Set multidate to true for unlimited picks, or to a number to cap them. Once the cap is exceeded, the oldest date drops off the list.

The input holds the dates joined by multidateSeparator, a comma by default. Avoid a separator that appears inside the format, such as a hyphen with yyyy-mm-dd.

Can the calendar show only months or only years?

Yes. Set minViewMode to “months” or “years” and pair it with a matching format such as mm/yyyy.

The plugin fills in what the user cannot pick: the day becomes the 1st for months, and the month becomes January for years.

How do you stop the popup from opening when the input gets focus?

Set showOnFocus to false, then open the popup yourself with the show method, for example from a button click.

A separate option, enableOnReadonly, defaults to true. Switching it to false keeps the popup closed on readonly inputs.

How do you keep the phone keyboard from opening with the calendar?

Set disableTouchKeyboard to true. No keyboard then shows on mobile devices, so the calendar becomes the only way to enter a date on touch screens.

The option arrived in 1.4.0, so older copies ignore it.

Can the previous and next arrows be replaced with icons?

Yes. The templates option takes leftArrow and rightArrow strings, and each can hold plain text or HTML.

The defaults are double angle quotation marks. An icon font tag from Font Awesome or Bootstrap Icons works in their place.

How do you highlight weekends without disabling them?

Pass ‘0,6’ or [0,6] to daysOfWeekHighlighted, where 0 is Sunday and 6 is Saturday. Unlike daysOfWeekDisabled, the days stay selectable.

The setDaysOfWeekHighlighted method changes the list after initialization.

Migrating Off jQuery Without Breaking Date Fields

Replacing the uxsolutions Bootstrap datepicker with a jQuery-free picker means rewriting the calls around the widget, not the widget itself. Every .on(‘changeDate’) handler, .datepicker(‘method’) call and input-daterange block is written against jQuery and needs new code.

Begin with a list of every picker call and handler, since that sizes the job. Then store dates as yyyy-mm-dd strings. A native date input also submits yyyy-mm-dd, so saved values survive whichever picker replaces the plugin. After that, swap one form at a time with jQuery still loaded.

The cost is a stretch where two pickers live on one site, and each needs its own check of locale output and keyboard behavior.

Verified 28 October 2026: 1.10.1 is the latest tagged release. A release with Bootstrap 5 stylesheets would remove one reason to move, though jQuery would remain a dependency.