Alpine Calendar

A lightweight, feature-rich calendar component for Alpine.js. Inline & popup display, range selection, date constraints, input masking, and CSS custom property theming.

Alpine.js 3+ CSS Theming Zero Dependencies WCAG 2.1 AA Livewire Ready
npm i @reachweb/alpine-calendar
📅 Single / Range / Multiple
Inline & Popup
🔒 Date Constraints
🎨 Full Theming
Keyboard Nav
💰 Date Metadata

Basic Selection Modes

Single date, range, and multiple selection — all inline with zero config.

Single Date
Click to select one date. The simplest configuration.
View Code
<div x-data="calendar({ mode: 'single' })"></div>
Range Selection
Select a start and end date. Hover to preview the range.
View Code
<div x-data="calendar({ mode: 'range' })"></div>
Multiple Selection
Toggle multiple individual dates on and off.
View Code
<div x-data="calendar({ mode: 'multiple' })"></div>
Sunday Start
Override the default Monday start with firstDay: 0.
View Code
<div x-data="calendar({ mode: 'single', firstDay: 0 })"></div>
2-Month Range
Display two months side-by-side for easier range picking.
View Code
<div x-data="calendar({ mode: 'range', months: 2 })"></div>

Presets & Programmatic Control

Pre-select dates via config or change them dynamically with setValue() and clear().

Pre-selected Date
Calendar opens with March 15, 2026 already selected.
Pre-selected: 2026-03-15
View Code
<div x-data="calendar({
  mode: 'single',
  value: '2026-03-15'
})"></div>
Dynamic setValue()
Set or clear the selection programmatically.
View Code
<div x-data="calendar({ mode: 'single' })" x-ref="cal"></div>
<button @click="$refs.cal.setValue('2026-06-15')">Set June 15</button>
<button @click="$refs.cal.setValue('2026-12-25')">Set Dec 25</button>
<button @click="$refs.cal.clear()">Clear</button>
Pre-selected Range
Initialize with a date range already chosen.
Range: 2026-03-10 to 2026-03-20
View Code
<div x-data="calendar({
  mode: 'range',
  value: '2026-03-10 - 2026-03-20'
})"></div>
Form Submission
Hidden inputs are created automatically for form submission.
View Code
<form @submit.prevent="handleSubmit">
  <div x-data="calendar({
    mode: 'single',
    name: 'appointment'
  })"></div>
  <button type="submit">Submit</button>
</form>

Date Constraints

Restrict selection with min/max dates, disabled weekdays, specific dates, and range limits.

Min/Max & No Weekends
Only weekdays between Feb 1 and Apr 30, 2026.
Feb 1 – Apr 30 | Weekdays only
View Code
<div x-data="calendar({
  mode: 'single',
  minDate: '2026-02-01',
  maxDate: '2026-04-30',
  disabledDaysOfWeek: [0, 6],
})"></div>
Disabled Dates with Exceptions
Weekends off, but Mar 7-8 force-enabled. Mar 9-11 force-disabled.
View Code
<div x-data="calendar({
  mode: 'single',
  disabledDaysOfWeek: [0, 6],
  enabledDates: ['2026-03-07', '2026-03-08'],
  disabledDates: ['2026-03-09', '2026-03-10', '2026-03-11'],
})"></div>
Range with Min/Max Days
Range must be 3-10 days. Invalid endpoints are dimmed on hover.
Min: 3 days | Max: 10 days
View Code
<div x-data="calendar({
  mode: 'range',
  minRange: 3,
  maxRange: 10,
})"></div>

Period Rules

Apply different constraint rules for specific date periods — perfect for seasonal booking systems.

Summer vs Winter Booking
Global: 2-14 days, Sundays disabled. Summer (Jun-Aug): 7-21 days, all days open. Holidays (Dec 20-Jan 5): 3-7 days, all days open.
View Code
<div x-data="calendar({
  mode: 'range',
  months: 2,
  minRange: 2,
  maxRange: 14,
  disabledDaysOfWeek: [0],
  rules: [
    {
      from: '2026-06-01', to: '2026-08-31',
      minRange: 7, maxRange: 21,
      disabledDaysOfWeek: [],
    },
    {
      from: '2026-12-20', to: '2027-01-05',
      minRange: 3, maxRange: 7,
      enabledDaysOfWeek: [1, 2, 3, 4, 5, 6, 0],
    }
  ],
})"></div>

Wizard Mode

Step-by-step date selection: Year → Month → Day. Great for birth dates and historical dates.

Full Wizard (Birth Date)
Year → Month → Day step flow.
View Code
<div x-data="calendar({
  mode: 'single',
  wizard: true})"></div>
Year-Month Wizard
Start from year view, skip to month, then days.
View Code
<div x-data="calendar({
  mode: 'single',
  wizard: 'year-month'})"></div>
Month-Day Wizard
Start from month view, then pick a day.
View Code
<div x-data="calendar({
  mode: 'single',
  wizard: 'month-day'})"></div>
Popup Wizard
Full wizard inside a popup dropdown.
View Code
<div x-data="calendar({
  mode: 'single',
  wizard: true,
  display: 'popup'})">
  <input x-ref="rc-input" type="text" class="rc-input"
         placeholder="Birth date...">
</div>

Date Metadata

Attach labels, pricing, availability, and custom colors to individual dates. Uses a static map or dynamic callback.

Room Pricing (Static)
Labels show nightly price. Green dots = available, strikethrough = unavailable.
View Code
<div x-data="calendar({
  mode: 'single',
  dateMetadata: {
    '2026-03-01': { label: '$120', availability: 'available' },
    '2026-03-03': { label: '$150', availability: 'available', color: '#16a34a' },
    '2026-03-05': { label: '$180', availability: 'available', color: '#ea580c' },
    '2026-03-06': { availability: 'unavailable' },
    '2026-03-07': { label: 'Sold', availability: 'unavailable' },
    '2026-03-09': { label: '$99', availability: 'available', color: '#2563eb' },
  }
})"></div>
Dynamic Pricing (Callback)
Weekdays available with computed price, weekends unavailable.
View Code
<div x-data="calendar({
  mode: 'range',
  dateMetadata: (date) => {
    const d = date.toNativeDate().getDay()
    if (d === 0 || d === 6)
      return { availability: 'unavailable' }
    return {
      label: '$' + (100 + date.day * 3),
      availability: 'available'
    }
  }
})"></div>

Multi-Month Scrollable

Display 3+ months in a scrollable container. Great for long-range booking flows.

3-Month Single
Scroll through three months inline.
View Code
<div x-data="calendar({
  mode: 'single',
  months: 3})"></div>
12-Month Range
A full year of scrollable range selection.
View Code
<div x-data="calendar({
  mode: 'range',
  months: 12,
  scrollHeight: 400
})"></div>

Theming

Fully customizable via CSS custom properties. Override any --color-calendar-* variable.

Dark Theme
Override surface, text, and border colors.
View Code
.dark-calendar {
  --color-calendar-bg: #1e293b;
  --color-calendar-text: #f1f5f9;
  --color-calendar-border: #334155;
  --color-calendar-hover: #334155;
  --color-calendar-primary: #818cf8;
  --color-calendar-primary-text: #1e293b;
  --color-calendar-today-ring: #818cf8;
  --color-calendar-range: #312e81;
}
Custom Accent
Change the primary color to match your brand.
View Code
.green-theme {
  --color-calendar-primary: #059669;
  --color-calendar-primary-text: #ffffff;
  --color-calendar-today-ring: #34d399;
  --color-calendar-range: #ecfdf5;
}