Starting Point
v0.35.0

Search documentation

Search for a page and jump to it.

Start typing to search...
Go to Page
escClose

Tailwind CSS Calendar

Use the calendar component to pick dates from a month grid on the page, like a booking flow, an availability view, or a schedule. For a calendar behind a form field, use the date picker.

<div class="calendar rounded-lg border" data-sp-caption="dropdown">
  <input type="date" name="date" />
</div>

Usage

Add the calendar class to a container and put a native <input type="date"> inside it. The calendar hides the input and writes the picked date to it, so the value submits with the form like any other field. Add a second input for a date range, or give the input the multiple attribute for several dates.

ClassDescription
calendarAdd to the container holding the date inputs, which carries the data-sp-* options
calendar-previousRendered previous month button
calendar-nextRendered next month button
calendar-captionRendered month label, or the month and year dropdowns
calendar-weekdayRendered weekday header
calendar-week-numberRendered week number cell
calendar-cellRendered day cell, with selected, today, outside, and disabled modifiers
calendar-dayRendered day button inside a cell
<div class="calendar">
  <input type="date" name="date" />
</div>

Examples

Basic calendar

One date input makes a single date calendar, styled here with rounded-lg border.

<div class="calendar rounded-lg border">
  <input type="date" name="date" />
</div>

Range calendar

Two date inputs make a range, and data-sp-months="2" shows two months side by side.

<div class="calendar rounded-lg border" data-sp-months="2">
  <input type="date" name="from" value="2026-01-12" />
  <input type="date" name="to" value="2026-02-11" />
</div>

Calendar with multiple dates

Add multiple to the input to pick several dates. Each picked date submits as a hidden input under the same name, and hidden inputs you render with that name start out picked.

<div class="card w-fit p-0">
  <div class="card-content p-0">
    <div class="calendar">
      <input type="date" name="dates" multiple />
    </div>
  </div>
</div>

Month and year selector

Set data-sp-caption="dropdown" to swap the month label for month and year dropdowns.

<div class="calendar rounded-lg border" data-sp-caption="dropdown">
  <input type="date" name="date" />
</div>

Calendar presets

Call select() with an ISO date to pick it from outside the calendar, like preset buttons.

<div class="card card-sm w-fit max-w-[300px]">
  <div class="card-content">
    <div id="calendar-presets" class="calendar p-0 [--calendar-cell-size:--spacing(9.5)]">
      <input type="date" name="date" />
    </div>
  </div>
  <div class="card-footer flex-wrap gap-2 border-t">
    <button class="btn btn-outline btn-sm flex-1" data-days="0">Today</button>
    <button class="btn btn-outline btn-sm flex-1" data-days="1">Tomorrow</button>
    <button class="btn btn-outline btn-sm flex-1" data-days="3">In 3 days</button>
    <button class="btn btn-outline btn-sm flex-1" data-days="7">In a week</button>
    <button class="btn btn-outline btn-sm flex-1" data-days="14">In 2 weeks</button>
  </div>
</div>
 
<script>
  const calendar = document.getElementById("calendar-presets");
  document.querySelectorAll("[data-days]").forEach((button) => {
    button.addEventListener("click", () => {
      const date = new Date();
      date.setDate(date.getDate() + Number(button.dataset.days));
      sp.calendar(calendar).select(date.toLocaleDateString("en-CA"));
    });
  });
</script>

Date and time picker

Pair the calendar with native time inputs. The date and the times submit as separate fields.

<div class="card card-sm w-fit">
  <div class="card-content">
    <div class="calendar p-0">
      <input type="date" name="date" value="2026-09-12" />
    </div>
  </div>
  <div class="card-footer border-t bg-card">
    <div class="field-group">
      <div class="field">
        <label class="label" for="calendar-time-from">Start Time</label>
        <div class="input-group">
          <input
            class="input appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
            id="calendar-time-from"
            type="time"
            name="from"
            step="1"
            value="10:30:00"
          />
          <span class="input-group-addon"><svg class="text-muted-foreground" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4-2"/></svg></span>
        </div>
      </div>
      <div class="field">
        <label class="label" for="calendar-time-to">End Time</label>
        <div class="input-group">
          <input
            class="input appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
            id="calendar-time-to"
            type="time"
            name="to"
            step="1"
            value="12:30:00"
          />
          <span class="input-group-addon"><svg class="text-muted-foreground" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4-2"/></svg></span>
        </div>
      </div>
    </div>
  </div>
</div>

Calendar with booked dates

List dates or ranges in data-sp-disabled to block them, here struck through with line-through on the blocked days.

<div class="card w-fit p-0">
  <div class="card-content p-0">
    <div
      class="calendar [&_.disabled]:opacity-100 [&_.disabled_.calendar-day]:line-through"
      data-sp-disabled="2026-01-12..2026-01-26"
    >
      <input type="date" name="date" value="2026-01-06" />
    </div>
  </div>
</div>

Calendar min and max dates

Bound the pickable range with data-sp-min and data-sp-max, both an ISO date or today. Weekday names in data-sp-disabled block every Saturday and Sunday.

<div
  class="calendar rounded-lg border"
  data-sp-month="2026-03"
  data-sp-min="2026-03-02"
  data-sp-max="2026-03-27"
  data-sp-disabled="sat,sun"
>
  <input type="date" name="date" />
</div>

Calendar cell size

Size the cells with the --calendar-cell-size variable, responsive breakpoints included.

<div class="card w-fit p-0">
  <div class="card-content p-0">
    <div
      class="calendar [--calendar-cell-size:--spacing(10)] md:[--calendar-cell-size:--spacing(12)]"
      data-sp-caption="dropdown"
    >
      <input type="date" name="from" value="2026-12-08" />
      <input type="date" name="to" value="2026-12-18" />
    </div>
  </div>
</div>

Calendar week numbers

Add data-sp-week-numbers for an ISO week column, and data-sp-week-start="1" to start weeks on Monday.

<div class="card w-fit p-0">
  <div class="card-content p-0">
    <div class="calendar" data-sp-week-numbers data-sp-week-start="1">
      <input type="date" name="date" value="2026-01-12" />
    </div>
  </div>
</div>

Calendar locale

Set data-sp-locale to format month and weekday names in another language. It defaults to the page's lang.

<div class="calendar rounded-lg border" data-sp-locale="sv-SE" data-sp-week-start="1">
  <input type="date" name="datum" value="2026-06-06" />
</div>

RTL

Add dir="rtl" to any parent element, typically <html>, to render right-to-left. Pair it with a matching data-sp-locale. See the RTL guide for details.

<div id="calendar-rtl-demo" dir="rtl">
  <div
    class="calendar rounded-lg border [--calendar-cell-size:--spacing(9)]"
    data-sp-caption="dropdown"
    data-sp-locale="ar-EG"
  >
    <input type="date" name="date" />
  </div>
</div>
 
<script>
  const demo = document.getElementById("calendar-rtl-demo");
  const template = demo.innerHTML;
  const locales = {
    ar: { dir: "rtl", locale: "ar-EG" },
    he: { dir: "rtl", locale: "he-IL" },
    en: { dir: "ltr", locale: "en-US" },
  };
  window.addEventListener("message", (e) => {
    const t = locales[e.data?.lang];
    if (e.data?.type !== "sp-language" || !t) return;
    demo.dir = t.dir;
    demo.innerHTML = template;
    demo.querySelector(".calendar").setAttribute("data-sp-locale", t.locale);
  });
</script>

CSS variables

Set these on the calendar element.

VariableDefault
--calendar-cell-size--spacing(8)
--calendar-cell-radiusvar(--radius-md)
<div class="calendar [--calendar-cell-size:--spacing(10)]">
  <input type="date" name="date" />
</div>

Options

Set each option with its own data-sp-* attribute on the calendar element.

AttributeValuesDefault
data-sp-monthYYYY-MMThe selected or current month
data-sp-monthsNumber1
data-sp-captionlabel | dropdownlabel
data-sp-week-numbersBooleanfalse
data-sp-week-start0 (Sunday) to 6 (Saturday)0
data-sp-localeBCP 47 language tagThe page lang
data-sp-minYYYY-MM-DD | todayNone
data-sp-maxYYYY-MM-DD | todayNone
data-sp-disabledComma-separated dates, from..to ranges, weekday namesNone
<div
  class="calendar"
  data-sp-caption="dropdown"
  data-sp-min="today"
  data-sp-disabled="sat,sun,2026-12-24..2026-12-26"
>
  <input type="date" name="date" />
</div>

data-sp-min and data-sp-max are also copied onto the inputs, so the browser validates them on submit. Dates in data-sp-disabled are not, so check those on the server. required works like on any input, and it also keeps a second click on the picked day from clearing a single date.

JavaScript

Events

sp-change fires on the calendar element after every pick. Its detail is { value } for a single date, { from, to } for a range, and { values } for multiple dates. The inputs also fire native input and change events.

EventWhen
sp-changeThe selection changed
el.addEventListener("sp-change", (e) => {
  console.log(e.detail.value);
});

Methods

Get the instance with sp.calendar(el), then call its methods:

MethodDescription
value()The ISO value, { from, to } for a range, array for multiple
select(value)Select an ISO date, { from, to }, or an array of dates
clear()Clear the selection
goTo(month)Show the month holding a YYYY-MM or ISO date
focus()Move focus into the grid
const calendar = sp.calendar(document.querySelector("#my-calendar"));
 
calendar.select("2026-12-24");