Components
Datetime picker
A date and a time picked together from one field, for scheduling events, publishing posts, setting reminders or booking a window of time. For a date alone or a time alone, the date picker and the time picker serve better.
Drawn and behaving as Flux UI's datetime picker does, measured against its live docs.
Datetime picker
A day from the calendar and a time from the list beside it: the time closes it, a day alone changes the value at once.
using System.Globalization;
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerExample : Component
{
private DateTime _at = TimeProvider.System.GetLocalNow().Date.AddDays(5).AddHours(10).AddMinutes(30);
protected override Component? Render() =>
Div.Class("max-w-xs space-y-3")[
Ui.DatetimePicker.Value(_at).OnChange(at => { _at = at; }),
Ui.Text.Data("testid", "ui-datetime-picker-state")[_at == default ? "Nothing chosen." : "Chosen: " + _at.ToString("yyyy-MM-dd'T'HH:mm", CultureInfo.InvariantCulture)]
];
}Chosen: 2026-10-31T10:30
Basic usage
Bound to a DateTime?: a wall-clock time, as a datetime-local field holds it.
using System.Globalization;
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerBoundExample : Component
{
private readonly Event _event = new();
protected override Component? Render() =>
Div.Class("max-w-xs space-y-3")[
Ui.DatetimePicker.Bind(() => _event.StartsAt).Live().Label("Starts at"),
Ui.Text[_event.StartsAt is { } at ? "Starts " + at.ToString("yyyy-MM-dd'T'HH:mm", CultureInfo.InvariantCulture) + "." : "Not scheduled."]
];
public sealed class Event
{
public DateTime? StartsAt { get; set; }
}
}Not scheduled.
Range picker
A UiDateTimeRange: each end gets its own time, and Select range applies it.
using System.Globalization;
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerRangeExample : Component
{
private UiDateTimeRange _schedule;
protected override Component? Render() =>
Div.Class("max-w-xs space-y-3")[
Ui.DatetimePicker.Range.Value(_schedule).OnChange(range => { _schedule = range; }),
Ui.Text.Data("testid", "ui-datetime-picker-range-state")[_schedule == default ? "No window chosen." : "From " + At(_schedule.Start) + " to " + At(_schedule.End)]
];
private static string At(DateTime at) => at.ToString("yyyy-MM-dd'T'HH:mm", CultureInfo.InvariantCulture);
}No window chosen.
Range limits
At most seven days, the start and the end days included.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerRangeLimitsExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[Ui.DatetimePicker.Range.Value(default(UiDateTimeRange)).MaxRange(7)];
}Time input
An exact time typed in parts, with the list as suggestions; for a moment and for a range.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerInputExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs space-y-4")[
Ui.DatetimePicker.Value(default(DateTime)).Time(Ui.DatetimePickerTime.Input),
Ui.DatetimePicker.Range.Value(default(UiDateTimeRange)).Time(Ui.DatetimePickerTime.Input)
];
}Interval
A time every 15 minutes; 30 when unset.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerIntervalExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[Ui.DatetimePicker.Value(default(DateTime)).Interval(15)];
}Default time
Filled in as soon as a day is picked; for a range, the start's and the end's.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerDefaultTimeExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs space-y-4")[
Ui.DatetimePicker.Value(default(DateTime)).DefaultTime(new TimeOnly(9, 0)),
Ui.DatetimePicker.Range.Value(default(UiDateTimeRange)).DefaultTime(new UiTimeRange(new TimeOnly(9, 0), new TimeOnly(17, 0)))
];
}Min/max
From now to two weeks on at 5 PM: the days outside are off, and on the first and last day so are the times outside.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerMinMaxExample : Component
{
private readonly DateTime _now = TimeProvider.System.GetLocalNow().DateTime;
protected override Component? Render() =>
Div.Class("max-w-xs")[Ui.DatetimePicker.Value(default(DateTime)).Min(_now).Max(_now.Date.AddDays(14).AddHours(17))];
}Min/max times
Any future time between 9 AM and 5 PM: the times outside the window are not listed at all.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerMinMaxTimesExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.DatetimePicker.Value(default(DateTime)).Min(TimeProvider.System.GetLocalNow().DateTime).MinTime(new TimeOnly(9, 0)).MaxTime(new TimeOnly(17, 0))
];
}Unavailable dates and times
A whole day, a stretch of every day, and times of one day only. A stretch includes both its ends.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerUnavailableExample : Component
{
private readonly DateOnly _today = DateOnly.FromDateTime(TimeProvider.System.GetLocalNow().Date);
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.DatetimePicker.Value(default(DateTime)).Min(_today.ToDateTime(TimeOnly.MinValue)).MinTime(new TimeOnly(8, 0)).MaxTime(new TimeOnly(18, 0)).Unavailable([
_today.AddDays(2),
new UiTimeRange(new TimeOnly(12, 0), new TimeOnly(13, 0)),
_today.AddDays(1).ToDateTime(new TimeOnly(10, 0)),
UiDatetimeSlot.On(_today.AddDays(1), new TimeOnly(15, 0), new TimeOnly(16, 0)),
])
];
}Time format
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerFormatExample : Component
{
protected override Component? Render() =>
Div.Class("flex flex-wrap gap-4")[
Ui.DatetimePicker.Value(default(DateTime)).Label("12-hour").TimeFormat(Ui.DatetimePickerTimeFormat.TwelveHour),
Ui.DatetimePicker.Value(default(DateTime)).Label("24-hour").TimeFormat(Ui.DatetimePickerTimeFormat.TwentyFourHour)
];
}Clearable
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerClearableExample : Component
{
private DateTime? _at = TimeProvider.System.GetLocalNow().Date.AddDays(3).AddHours(9);
protected override Component? Render() =>
Div.Class("max-w-xs space-y-3")[
Ui.DatetimePicker.Value(_at ?? default).OnChange(at => { _at = at == default ? null : at; }).Clearable(),
Ui.Text.Data("testid", "ui-datetime-picker-clear-state")[_at is null ? "Nothing chosen." : "A moment is chosen."]
];
}A moment is chosen.
With confirmation
Nothing is written until Select; Cancel, Escape and a press outside drop the choice.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerConfirmationExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[Ui.DatetimePicker.Value(default(DateTime)).WithConfirmation()];
}Custom trigger
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerTriggerExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.DatetimePicker.Value(default(DateTime)).Trigger(Ui.Button.Icon(Ui.IconName.CalendarDays)["Schedule"])
];
}Calendar options
The date picker's own: the today shortcut, a selectable header, week numbers, the start day and fixed weeks.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerCalendarExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[Ui.DatetimePicker.Value(default(DateTime)).WithToday().SelectableHeader().WeekNumbers()];
}Open to
A moment to open on while nothing is chosen, or only a time: this month, the list scrolled to it.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerOpenToExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs space-y-4")[
Ui.DatetimePicker.Value(default(DateTime)).OpenTo(new DateTime(2026, 12, 1, 9, 0, 0, DateTimeKind.Unspecified)),
Ui.DatetimePicker.Value(default(DateTime)).OpenTo(new TimeOnly(9, 0))
];
}Localization
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerLocaleExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[Ui.DatetimePicker.Value(default(DateTime)).Locale("fr-FR")];
}Separate date and time
A date picker and a time picker side by side, for a date and a time kept apart.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatetimePickerSeparateExample : Component
{
private readonly Meeting _meeting = new();
public sealed class Meeting
{
public DateOnly? Day { get; set; }
public TimeOnly? At { get; set; }
}
protected override Component? Render() =>
Div.Class("flex max-sm:flex-col gap-2 w-full max-w-64 sm:max-w-96")[
Ui.DatePicker.Bind(() => _meeting.Day).Class("flex-1"),
Ui.TimePicker.Bind(() => _meeting.At).Class("sm:w-40")
];
}Behaviour
What it binds
Ui.DatetimePicker binds a DateTime or a DateTime?, Ui.DatetimePicker.Range a UiDateTimeRange — Flux's DateTimeRange: a Start and an End, HasStart and HasEnd, written in an address as 2026-10-14T22:00..2026-10-15T02:00. Precise to the minute: seconds are dropped. A bound value is written with the reader's next action, at once with .Live().
Unavailable takes UiDatetimeSlots, which a day (DateOnly), a stretch of every day (TimeOnly, UiTimeRange) and a time of one day (DateTime, UiDatetimeSlot.On(day, from, to)) each convert to, as Flux mixes them in one list. OpenTo takes a moment, a day or only a time.
When it writes
Picking a time closes the picker and writes the moment; changing only the day writes it at once and leaves the popup open. A day alone or a time alone is held until the other is there, and DefaultTime fills the time in as soon as a day is picked. A range, typed times and WithConfirmation() wait for Select instead; Cancel, Escape and a press outside drop what was held. A range whose end is not after its start says so and cannot be selected.
All of it is the browser's, as Flux's script keeps it: paging, the arrows, the times a day rules out, what the button says. The page hears the value, and nothing else.
Time zones
Wall-clock times, as a datetime-local field holds them: 2026-10-14T10:30 is half past ten that day and is not converted between zones. For readers in other zones, convert to and from theirs yourself — TimeZoneInfo.ConvertTimeToUtc(startsAt, reader) on the way in. Min and Max are moments of the server's clock: Flux's min="now" is .Min(DateTime.Now) of the clock you trust.
On a phone
Under 640 px it is the date picker's sheet: one month, the time a field under it with its own list, and Cancel and Select at its foot. Nothing is written until Select.
The keyboard
The calendar's keys are the date picker's. Tab goes from the month steps to the day, then to the time the column opens on.
Key | What happens |
|---|---|
| ArrowDown, ArrowUp | In the column of times: the next or the previous time that can be picked, stopping at the ends. |
| Home, End | In the column of times: the first or the last. |
| Enter, Space | Picks the time in focus. |
| Escape | Closes, and drops what was held. |
Reference
Ui.DatetimePicker
A field-shaped button over a calendar and a list of times in a popup. Ui.DatetimePicker picks a moment, Ui.DatetimePicker.Range a start and an end.
Step | Takes | What it does |
|---|---|---|
.Bind(…) | () => a DateTime, or a UiDateTimeRange | The member it reads and writes: () => m.StartsAt, or a UiDateTimeRange. Or Value with OnChange. |
.Value(…) | a DateTime, or a UiDateTimeRange | What it holds, for a page that holds it itself. |
.OnChange(…) | a handler given the new value | Runs with the new value when it is chosen. |
.AfterBind(…) | a handler given the new value | Runs after Bind has written the model. |
.Validate(…) | a validator | A rule the value is checked by when it is written. |
.Live() | bool | Writes the model as soon as it is chosen, not with the next action. |
.Mode(…) | Ui.DatetimePickerModeSingle Range Each is a step of its own too, unless an HTML tag has its name. | What is picked, as a value. The opening step says it; this has to agree with what is bound. |
.Time(…) | Ui.DatetimePickerTimeList Input Each is a step of its own too, unless an HTML tag has its name. | How times are chosen: from a list, or typed (Ui.DatetimePickerTime.Input). The list when unset. |
.Interval(…) | int | Minutes between the times in the list. 30 when unset. |
.TimeFormat(…) | Ui.DatetimePickerTimeFormatAuto TwelveHour TwentyFourHour Each is a step of its own too, unless an HTML tag has its name. | 12-hour or 24-hour. The locale's own when unset. |
.Min(…) | a DateTime | The earliest moment that can be picked: earlier days are off, and so are the earlier times of its day. |
.Max(…) | a DateTime | The latest moment that can be picked. |
.MinTime(…) | a TimeOnly | The earliest time of day listed, on every day. |
.MaxTime(…) | a TimeOnly | The latest time of day listed, on every day. |
.Unavailable(…) | UiDatetimeSlot values: [day, new UiTimeRange(from, to), moment] | Days, stretches of every day, and times of one day that cannot be picked. |
.DefaultTime(…) | a TimeOnly, or a UiTimeRange | The time filled in as soon as a day is picked; for a range, the start's and the end's. |
.MinRange(…) | int | The fewest days a range may span, its ends included. |
.MaxRange(…) | int | The most days a range may span, its ends included. |
.OpenTo(…) | a DateTime, a DateOnly or a TimeOnly | Where it opens while nothing is chosen: a moment, a day, or a time of today. |
.Months(…) | int | How many months are shown side by side. One, or two for a range, when unset. |
.Size(…) | Ui.DatetimePickerSizeDefault Xs Sm Lg Xl Xxl Each is a step of its own too, unless an HTML tag has its name. | How large the trigger and the calendar's day cells are. |
.StartDay(…) | DayOfWeekSunday Monday Tuesday Wednesday Thursday Friday Saturday Each is a step of its own too, unless an HTML tag has its name. | The day the week starts on. The locale's own when unset. |
.WeekNumbers() | bool | Shows each week's number. |
.SelectableHeader() | bool | Makes the month and the year in the header selects. |
.WithToday() | bool | A button that comes back to this month, and picks today once there. |
.FixedWeeks() | bool | Always six weeks, so paging never changes the height. |
.WithConfirmation() | bool | Holds the choice until the reader presses Select. Always so for a range and typed times. |
.Clearable() | bool | A button in the trigger that empties the picker, while something is chosen. |
.Trigger(…) | Component | A trigger of your own: a Ui.Button, or a Ui.DatetimePickerButton. |
.Label(…) | string | Draws a field around it, with this label and the error. |
.Description(…) | string | Help text between the label and the picker. |
.DescriptionTrailing(…) | string | Help text under the picker. |
.Badge(…) | string | A word beside the label: "Optional". |
.Placeholder(…) | string | What the button shows while nothing is chosen. By mode when unset. |
.Disabled() | bool | A picker that cannot be opened. |
.Invalid() | bool | Error styling the form did not ask for. |
.Locale(…) | string | The locale the names, the week, the dates and the clock follow. The app's culture when unset. |
.ShowValidation() | bool | False leaves the member's message to a Ui.Error placed elsewhere. |
Ui.DatetimePickerButton
The field-shaped button in a picker's Trigger, for what the picker's own steps do not reach.
Step | Takes | What it does |
|---|---|---|
.Placeholder(…) | string | What it shows while nothing is chosen. The picker's own when unset. |
.Size(…) | Ui.DatetimePickerButtonSizeBase Sm Xs Each is a step of its own too, unless an HTML tag has its name. | How tall it is. |
.Clearable() | bool | A button that empties the picker, while something is chosen. The picker's own when unset. |
.Invalid() | bool | Draws the button as holding an error. |