Date Picker
A date, date+time, time, or date-range picker with a two-month calendar view, month/year dropdowns, full Tailwind class control, and no built-in visual styles.
Overview
MDatePicker supports single-value modes (Date, DateTime,
Time) and a date-range mode (IsDateRange="true"). All bind to
DateTime?; range mode adds an EndValue binding for the end date.
The calendar header provides month and year <select> dropdowns for quick
navigation alongside prev/next buttons. The panel is fully unstyled — every surface accepts
a dedicated class parameter. It opens on trigger click and closes on Escape or after selection.
Dates outside Min/Max are disabled.
Usage
Add the namespace import and include the stylesheet reference if not already done.
@using Marai.UI.Components.MDatePickerDate Only
The default mode. Opens a two-month calendar and commits the selected date at midnight. The panel closes automatically when a date is chosen.
Selected: none
<div class="flex flex-col gap-2">
<MLabel For="date-only">Pick a date</MLabel>
<MDatePicker id="date-only"
@bind-Value="_date"
Placeholder="Select a date"
Class="relative inline-block"
TriggerClass="w-64 rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-left text-slate-700 hover:border-slate-400 focus:outline-none focus:ring-2 focus:ring-sky-500"
BackdropClass="z-[9]"
PanelClass="absolute z-10 mt-1 rounded-xl border border-slate-200 bg-white p-4 shadow-lg"
MonthClass="w-56"
HeaderClass="flex items-center justify-between gap-1 mb-3"
NavButtonClass="flex h-7 w-7 items-center justify-center rounded-md text-slate-500 hover:bg-slate-100 text-lg leading-none"
MonthSelectClass="flex-1 bg-transparent text-sm font-semibold text-slate-800 focus:outline-none cursor-pointer"
YearSelectClass="bg-transparent text-sm font-semibold text-slate-800 focus:outline-none cursor-pointer"
DayGridClass="gap-y-1 text-center"
DayOfWeekClass="text-xs font-medium text-slate-400 flex items-center justify-center h-8"
DayClass="flex h-8 w-full items-center justify-center rounded-md text-sm text-slate-700 hover:bg-sky-50 cursor-pointer"
SelectedDayClass="!bg-sky-600 !text-white hover:!bg-sky-700"
TodayDayClass="font-bold text-sky-600"
DisabledDayClass="cursor-not-allowed opacity-30 hover:bg-transparent" />
<p class="text-sm text-slate-500">Selected: @(_date.HasValue ? _date.Value.ToString("MMMM d, yyyy") : "none")</p>
</div>
@code {
private DateTime? _date;
}
Examples
Date + Time
Set Mode="MDatePickerMode.DateTime" to show the calendar alongside a native
input type="time". Selecting a new date preserves the current time component.
Use TimeInputClass to style the time field.
Selected: none
<div class="flex flex-col gap-2">
<MLabel For="date-time">Appointment</MLabel>
<MDatePicker id="date-time"
Mode="MDatePickerMode.DateTime"
@bind-Value="_dateTime"
Placeholder="Select date & time"
Class="relative inline-block"
TriggerClass="w-64 rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-left text-slate-700 hover:border-slate-400 focus:outline-none focus:ring-2 focus:ring-sky-500"
BackdropClass="z-[9]"
PanelClass="absolute z-10 mt-1 rounded-xl border border-slate-200 bg-white p-4 shadow-lg"
MonthClass="w-56"
HeaderClass="flex items-center justify-between gap-1 mb-3"
NavButtonClass="flex h-7 w-7 items-center justify-center rounded-md text-slate-500 hover:bg-slate-100 text-lg leading-none"
MonthSelectClass="flex-1 bg-transparent text-sm font-semibold text-slate-800 focus:outline-none cursor-pointer"
YearSelectClass="bg-transparent text-sm font-semibold text-slate-800 focus:outline-none cursor-pointer"
DayGridClass="gap-y-1 text-center"
DayOfWeekClass="text-xs font-medium text-slate-400 flex items-center justify-center h-8"
DayClass="flex h-8 w-full items-center justify-center rounded-md text-sm text-slate-700 hover:bg-sky-50 cursor-pointer"
SelectedDayClass="!bg-sky-600 !text-white hover:!bg-sky-700"
TodayDayClass="font-bold text-sky-600"
DisabledDayClass="cursor-not-allowed opacity-30 hover:bg-transparent"
TimeInputClass="mt-3 w-full rounded-md border border-slate-200 bg-slate-50 px-3 py-1.5 text-sm text-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500" />
<p class="text-sm text-slate-500">Selected: @(_dateTime.HasValue ? _dateTime.Value.ToString("MMM d, yyyy h:mm tt") : "none")</p>
</div>
@code {
private DateTime? _dateTime;
}
Time Only
Mode="MDatePickerMode.Time" renders only a native input type="time".
The bound value stores the chosen time on DateTime.Today. No calendar panel is
shown. Use TimeInputClass for all visual styling.
Selected: none
<div class="flex flex-col gap-2">
<MLabel For="time-only">Meeting time</MLabel>
<MDatePicker id="time-only"
Mode="MDatePickerMode.Time"
@bind-Value="_time"
TimeInputClass="w-40 rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 focus:outline-none focus:ring-2 focus:ring-sky-500" />
<p class="text-sm text-slate-500">Selected: @(_time.HasValue ? _time.Value.ToString("h:mm tt") : "none")</p>
</div>
@code {
private DateTime? _time;
}
Date Range
Set IsDateRange="true" and bind both @bind-Value (start) and
@bind-EndValue (end). The first click sets the start date; the second click sets
the end date and closes the panel. Clicking before the current start resets the selection.
While selecting the end date, hovering over days previews the range using
InRangeClass. Use RangeStartClass and RangeEndClass to
differentiate the endpoints visually.
Check-in: — → Check-out: —
<div class="flex flex-col gap-2">
<MLabel For="range">Book a stay</MLabel>
<MDatePicker id="range"
IsDateRange="true"
DisplayMonths="2"
@bind-Value="_start"
@bind-EndValue="_end"
Placeholder="Select check-in – check-out"
Class="relative inline-block"
TriggerClass="w-72 rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-left text-slate-700 hover:border-slate-400 focus:outline-none focus:ring-2 focus:ring-sky-500"
BackdropClass="z-[9]"
PanelClass="absolute z-10 mt-1 rounded-xl border border-slate-200 bg-white p-4 shadow-lg"
MonthClass="w-56"
HeaderClass="flex items-center justify-between gap-1 mb-3"
NavButtonClass="flex h-7 w-7 items-center justify-center rounded-md text-slate-500 hover:bg-slate-100 text-lg leading-none"
MonthSelectClass="flex-1 bg-transparent text-sm font-semibold text-slate-800 focus:outline-none cursor-pointer"
YearSelectClass="bg-transparent text-sm font-semibold text-slate-800 focus:outline-none cursor-pointer"
DayGridClass="gap-y-1 text-center"
DayOfWeekClass="text-xs font-medium text-slate-400 flex items-center justify-center h-8"
DayClass="flex h-8 w-full items-center justify-center rounded-md text-sm text-slate-700 hover:bg-sky-50 cursor-pointer"
SelectedDayClass="!bg-sky-600 !text-white hover:!bg-sky-700"
TodayDayClass="font-bold text-sky-600"
DisabledDayClass="cursor-not-allowed opacity-30 hover:bg-transparent"
RangeStartClass="!rounded-r-none"
RangeEndClass="!rounded-l-none"
InRangeClass="!rounded-none !bg-sky-100 !text-sky-800 hover:!bg-sky-200" />
<p class="text-sm text-slate-500">
Check-in: <strong>@(_start.HasValue ? _start.Value.ToString("MMM d, yyyy") : "—")</strong>
→
Check-out: <strong>@(_end.HasValue ? _end.Value.ToString("MMM d, yyyy") : "—")</strong>
</p>
</div>
@code {
private DateTime? _start;
private DateTime? _end;
}
Min / Max Constraints
Set Min and/or Max to restrict the selectable range. Days outside
the range render with the native disabled attribute. Apply visual disabled
styling (strikethrough, reduced opacity) via DisabledDayClass.
Range: Jun 1 – Jun 30, 2026
Selected: none
<div class="flex flex-col gap-2">
<MLabel For="min-max">Book within this month</MLabel>
<MDatePicker id="min-max"
@bind-Value="_date"
Min="_min"
Max="_max"
Placeholder="Select a date"
Class="relative inline-block"
TriggerClass="w-64 rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-left text-slate-700 hover:border-slate-400 focus:outline-none focus:ring-2 focus:ring-sky-500"
BackdropClass="z-[9]"
PanelClass="absolute z-10 mt-1 rounded-xl border border-slate-200 bg-white p-4 shadow-lg"
MonthClass="w-56"
HeaderClass="flex items-center justify-between gap-1 mb-3"
NavButtonClass="flex h-7 w-7 items-center justify-center rounded-md text-slate-500 hover:bg-slate-100 text-lg leading-none"
MonthSelectClass="flex-1 bg-transparent text-sm font-semibold text-slate-800 focus:outline-none cursor-pointer"
YearSelectClass="bg-transparent text-sm font-semibold text-slate-800 focus:outline-none cursor-pointer"
DayGridClass="gap-y-1 text-center"
DayOfWeekClass="text-xs font-medium text-slate-400 flex items-center justify-center h-8"
DayClass="flex h-8 w-full items-center justify-center rounded-md text-sm text-slate-700 hover:bg-sky-50 cursor-pointer"
SelectedDayClass="!bg-sky-600 !text-white hover:!bg-sky-700"
TodayDayClass="font-bold text-sky-600"
DisabledDayClass="cursor-not-allowed opacity-30 line-through hover:bg-transparent" />
<p class="text-sm text-slate-500">
Range: @_min.ToString("MMM d") – @_max.ToString("MMM d, yyyy")
</p>
<p class="text-sm text-slate-500">Selected: @(_date.HasValue ? _date.Value.ToString("MMMM d, yyyy") : "none")</p>
</div>
@code {
private DateTime? _date;
private DateTime _min = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
private DateTime _max = new DateTime(DateTime.Today.Year, DateTime.Today.Month,
DateTime.DaysInMonth(DateTime.Today.Year, DateTime.Today.Month));
}
Full Tailwind Customization
Every visual surface — trigger, panel, month headers, nav buttons, day cells — accepts a dedicated class parameter. The example below builds a dark-themed picker entirely through Tailwind utilities.
Selected: none
<div class="flex flex-col gap-2">
<MLabel For="custom">Dark themed picker</MLabel>
<MDatePicker id="custom"
@bind-Value="_date"
Placeholder="Choose a date"
Class="relative inline-block"
TriggerClass="w-64 rounded-lg bg-slate-800 px-4 py-2 text-sm text-left text-slate-100 border border-slate-600 hover:border-slate-400 focus:outline-none focus:ring-2 focus:ring-violet-500"
BackdropClass="z-[9]"
PanelClass="absolute z-10 mt-1 rounded-xl border border-slate-700 bg-slate-900 p-4 shadow-2xl"
MonthClass="w-56"
HeaderClass="flex items-center justify-between gap-1 mb-3"
NavButtonClass="flex h-7 w-7 items-center justify-center rounded-md text-slate-400 hover:bg-slate-700 hover:text-white text-lg leading-none"
MonthSelectClass="flex-1 bg-transparent text-sm font-semibold text-slate-200 focus:outline-none cursor-pointer"
YearSelectClass="bg-transparent text-sm font-semibold text-slate-200 focus:outline-none cursor-pointer"
DayGridClass="gap-y-1 text-center"
DayOfWeekClass="text-xs font-medium text-slate-500 flex items-center justify-center h-8"
DayClass="flex h-8 w-full items-center justify-center rounded-md text-sm text-slate-300 hover:bg-slate-700 cursor-pointer"
SelectedDayClass="!bg-violet-600 !text-white hover:!bg-violet-700"
TodayDayClass="font-bold !text-violet-400"
DisabledDayClass="cursor-not-allowed opacity-25 hover:bg-transparent" />
<p class="text-sm text-slate-500">Selected: @(_date.HasValue ? _date.Value.ToString("MMMM d, yyyy") : "none")</p>
</div>
@code {
private DateTime? _date;
}
Two-Way Binding
Use @bind-Value to keep a DateTime? field in sync. You can read
or write the value programmatically — the calendar view will follow the bound value.
Bound value: null
<div class="flex flex-col gap-3">
<MLabel For="binding">Start date</MLabel>
<MDatePicker id="binding"
@bind-Value="_date"
Placeholder="Pick a start date"
Class="relative inline-block"
TriggerClass="w-64 rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-left text-slate-700 hover:border-slate-400 focus:outline-none focus:ring-2 focus:ring-sky-500"
BackdropClass="z-[9]"
PanelClass="absolute z-10 mt-1 rounded-xl border border-slate-200 bg-white p-4 shadow-lg"
MonthClass="w-56"
HeaderClass="flex items-center justify-between gap-1 mb-3"
NavButtonClass="flex h-7 w-7 items-center justify-center rounded-md text-slate-500 hover:bg-slate-100 text-lg leading-none"
MonthSelectClass="flex-1 bg-transparent text-sm font-semibold text-slate-800 focus:outline-none cursor-pointer"
YearSelectClass="bg-transparent text-sm font-semibold text-slate-800 focus:outline-none cursor-pointer"
DayGridClass="gap-y-1 text-center"
DayOfWeekClass="text-xs font-medium text-slate-400 flex items-center justify-center h-8"
DayClass="flex h-8 w-full items-center justify-center rounded-md text-sm text-slate-700 hover:bg-sky-50 cursor-pointer"
SelectedDayClass="!bg-sky-600 !text-white hover:!bg-sky-700"
TodayDayClass="font-bold text-sky-600"
DisabledDayClass="cursor-not-allowed opacity-30 hover:bg-transparent" />
<div class="flex gap-2">
<button class="rounded-md border border-slate-300 px-3 py-1 text-sm hover:bg-slate-50"
@onclick="SetToday">
Set today
</button>
<button class="rounded-md border border-slate-300 px-3 py-1 text-sm hover:bg-slate-50"
@onclick="Clear">
Clear
</button>
</div>
<p class="text-sm text-slate-500">
Bound value: <strong>@(_date.HasValue ? _date.Value.ToString("yyyy-MM-dd") : "null")</strong>
</p>
</div>
@code {
private DateTime? _date;
private void SetToday() => _date = DateTime.Today;
private void Clear() => _date = null;
}
Accessibility
- The trigger button exposes
aria-expandedandaria-haspopup="dialog" - Each day button carries
aria-label(full date) andaria-pressed(selected state) - Navigation buttons have
aria-label="Previous month"andaria-label="Next month" - Pressing
Escapecloses the panel - Disabled dates use the native
disabledattribute — they are skipped by keyboard navigation automatically - Always pair the trigger with an
MLabelusing matchingidandForattributes
API Reference
MDatePicker
| Parameter | Type | Default | Description |
|---|---|---|---|
| Value | DateTime? | null |
The selected date/time. Use with @bind-Value for two-way binding. |
| ValueChanged | EventCallback<DateTime?> | — |
Callback invoked when the selection changes. Wired automatically by @bind-Value. |
| ValueExpression | Expression<Func<DateTime?>>? | null |
Expression identifying the bound field for <ValidationMessage>. Set automatically by @bind-Value. |
| IsDateRange | bool | false |
Enables range selection mode. The first click sets Value (start); the second click sets EndValue (end) and closes the panel. |
| EndValue | DateTime? | null |
The range end date. Only used when IsDateRange="true". Use with @bind-EndValue. |
| EndValueChanged | EventCallback<DateTime?> | — |
Callback invoked when the end date changes. Wired automatically by @bind-EndValue. |
| EndValueExpression | Expression<Func<DateTime?>>? | null |
Expression for <ValidationMessage> on the end date field. |
| Mode | MDatePickerMode | Date |
Controls what the picker renders: calendar only (Date), calendar + time input (DateTime), or time input only (Time). |
| DisplayMonths | int | 2 |
Number of adjacent months displayed in the calendar panel. |
| Min | DateTime? | null |
Earliest selectable date. Days before this are disabled. |
| Max | DateTime? | null |
Latest selectable date. Days after this are disabled. |
| Placeholder | string? | null |
Text shown in the trigger button when no value is selected. |
| Disabled | bool | false |
Disables the trigger button (or the time input in Time mode). |
| Class | string? | null |
Applied to the root container <div> in Date / DateTime modes. |
| TriggerClass | string? | null |
Applied to the trigger <button>. |
| PanelClass | string? | null |
Applied to the calendar panel container. |
| MonthClass | string? | null |
Applied to each individual month grid container. |
| HeaderClass | string? | null |
Applied to the month header row (title + nav buttons). |
| NavButtonClass | string? | null |
Applied to the previous/next month navigation buttons. |
| MonthSelectClass | string? | null |
Applied to the <select> element for month navigation in the calendar header. |
| YearSelectClass | string? | null |
Applied to the <select> element for year navigation in the calendar header. |
| DayGridClass | string? | null |
Applied to the 7-column grid container holding the day-of-week labels and day cells. The grid layout itself is always present; use this for gap, padding, or text alignment. |
| DayOfWeekClass | string? | null |
Applied to each day-of-week label span (Su, Mo, Tu …). |
| DayClass | string? | null |
Base class applied to every day button. |
| SelectedDayClass | string? | null |
Added to a day button when it is the selected date. |
| TodayDayClass | string? | null |
Added to the day button representing today's date. |
| OutsideDayClass | string? | null |
Reserved for styling days that fall outside the current month grid (e.g. padding cells). |
| DisabledDayClass | string? | null |
Added to day buttons that are disabled due to Min/Max constraints. |
| RangeStartClass | string? | null |
Additional class on the range start day button (IsDateRange only). Applied on top of SelectedDayClass. |
| RangeEndClass | string? | null |
Additional class on the range end day button (IsDateRange only). Applied on top of SelectedDayClass. |
| InRangeClass | string? | null |
Applied to day buttons that fall between the start and end dates. Also applied during hover preview while selecting the end date. |
| TimeInputClass | string? | null |
Applied to the input type="time" element in DateTime and Time modes. |
| AdditionalAttributes | Dictionary<string, object>? | null |
Arbitrary HTML attributes forwarded to the root element (<div> or <input type="time"> in Time mode). |
MDatePickerMode
| Value | Description |
|---|---|
| Date | Calendar only. Commits selected date at midnight. |
| DateTime | Calendar + time input. Preserves time when changing date. |
| Time | Time input only. Stores the time on DateTime.Today. |
- Always pair with
MLabelusing matchingidandForfor accessible association - Use
Class="relative inline-block"(or similar) on the root so the panel can beabsolute-positioned below the trigger - Add a
z-*class toPanelClassto ensure the panel layers above surrounding content - Use
@bind-Valuerather than manualValue+ValueChangedwiring - Supply
DisabledDayClasswith visual cues (opacity, strikethrough) so users understand why dates are unavailable - In
Timemode,AdditionalAttributesare forwarded to the<input type="time">directly — you can passid,aria-label, etc.