Components
Date picker
A day or a range picked from a calendar in a popup, for filtering data or scheduling events. For a date far in the past or the future, such as a birthday, a date input serves better.
Drawn and behaving as Flux UI's date picker does, measured against its live docs.
Date picker
A field-shaped button over the calendar: a day closes it on the pick.
using System.Globalization;
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerExample : Component
{
private DateOnly _date;
protected override Component? Render() =>
Div.Class("max-w-xs space-y-3")[
Ui.DatePicker.Value(_date).OnChange(date => { _date = date; }),
Ui.Text.Data("testid", "ui-date-picker-state")[_date == default ? "No date chosen." : "Chosen: " + Iso(_date)]
];
private static string Iso(DateOnly date) => date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
}No date chosen.
Basic usage
Bound to a DateOnly? that starts on today, as Flux's value does.
using System.Globalization;
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerBoundExample : Component
{
private readonly Post _post = new();
protected override Component? Render() =>
Div.Class("max-w-xs space-y-3")[
Ui.DatePicker.Bind(() => _post.PublishedAt).Live().Label("Publish on"),
Ui.Text[_post.PublishedAt is { } day ? "Publishes " + day.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) + "." : "Not scheduled."]
];
public sealed class Post
{
public DateOnly? PublishedAt { get; set; } = DateOnly.FromDateTime(TimeProvider.System.GetLocalNow().Date);
}
}Publishes 2026-10-22.
Input trigger
Month, day and year typed as one field: a part that is full moves on to the next, and the chevron opens the calendar.
using System.Globalization;
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerInputExample : Component
{
private DateOnly _date;
protected override Component? Render() =>
Div.Class("max-w-xs space-y-3")[
Ui.DatePicker.Value(_date).OnChange(date => { _date = date; }).Type(Ui.DatePickerType.Input),
Ui.Text.Data("testid", "ui-date-picker-input-state")[
_date == default ? "No date chosen." : "Chosen: " + _date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)
]
];
}No date chosen.
Range picker
Two months and a UiDateRange, closed by the second click.
using System.Globalization;
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerRangeExample : Component
{
private UiDateRange _range;
protected override Component? Render() =>
Div.Class("max-w-xs space-y-3")[
Ui.DatePicker.Range.Value(_range).OnChange(range => { _range = range; }),
Ui.Text.Data("testid", "ui-date-picker-range-state")[
_range == default ? "No range chosen." : $"Range: {Iso(_range.Start)} to {Iso(_range.End)}"
]
];
private static string Iso(DateOnly date) => date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
}No range chosen.
Range limits
At least three days, and at most ten.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerRangeLimitsExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs space-y-4")[
Ui.DatePicker.Range.Value(default(UiDateRange)).MinRange(3),
Ui.DatePicker.Range.Value(default(UiDateRange)).MaxRange(10)
];
}Range with inputs
A trigger of your own: a field for the start and one for the end, each typed into and each opening the calendar.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerRangeInputsExample : Component
{
protected override Component? Render() =>
Ui.DatePicker.Range.Value(default(UiDateRange)).Trigger(
Div.Class("flex flex-col gap-6 sm:flex-row sm:gap-4")[
Ui.DatePickerInput.Key("start").Label("Start"),
Ui.DatePickerInput.Key("end").Label("End")
]);
}With inputs
WithInputs() puts the start and the end over the months, to type into: drawn as parts, Flux's custom variant.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerWithInputsExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.DatePicker.Range.Value(default(UiDateRange)).WithInputs()
];
}Native inputs
The browser's own date fields in the same box, Flux's native variant: over the months, and as the trigger itself.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerNativeInputsExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs space-y-4")[
Ui.DatePicker.Range.Value(default(UiDateRange)).WithInputs(Ui.DatePickerInputVariant.Native),
Ui.DatePicker.Value(default(DateOnly)).Trigger(Ui.DatePickerInput.Native)
];
}Presets
Today, Yesterday, This Week, Last 7 Days, This Month, Year to Date and All Time, which starts at Min. A preset closes the picker.
using System.Globalization;
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerPresetsExample : Component
{
private UiDateRange _range;
protected override Component? Render() =>
Div.Class("max-w-xs space-y-3")[
// Min is where "All Time" starts.
Ui.DatePicker.Range.Value(_range).OnChange(range => { _range = range; }).WithPresets().Min(new DateOnly(2012, 1, 1)),
Ui.Text.Data("testid", "ui-date-picker-presets-state")[_range == default ? "No range chosen." : "Range: " + Named(_range)]
];
private static string Named(UiDateRange range) =>
range.Start.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) + " to " + range.End.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
}No range chosen.
Choosing the presets
Presets lists the ones you name, in your order. With Custom among them, a range that matches no other preset is "Custom Range".
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerPresetListExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.DatePicker.Range.Value(default(UiDateRange)).Min(new DateOnly(2012, 1, 1)).Presets([
Ui.DateRangePreset.Today, Ui.DateRangePreset.Yesterday, Ui.DateRangePreset.ThisWeek, Ui.DateRangePreset.Last7Days,
Ui.DateRangePreset.ThisMonth, Ui.DateRangePreset.YearToDate, Ui.DateRangePreset.AllTime, Ui.DateRangePreset.Custom,
])
];
}Unavailable dates
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerUnavailableExample : Component
{
private static readonly DateOnly Today = DateOnly.FromDateTime(TimeProvider.System.GetLocalNow().Date);
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.DatePicker.Value(default(DateOnly)).Unavailable([Today.AddDays(-1), Today.AddDays(1)])
];
}With today shortcut
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerTodayExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.DatePicker.Value(default(DateOnly)).WithToday()
];
}Selectable header
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerHeaderExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.DatePicker.Value(default(DateOnly)).SelectableHeader()
];
}Fixed weeks
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerFixedWeeksExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.DatePicker.Value(default(DateOnly)).FixedWeeks()
];
}Start day
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerStartDayExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.DatePicker.Value(default(DateOnly)).StartDay(DayOfWeek.Monday)
];
}Open to
Where the calendar opens while nothing is chosen; with ForceOpenTo, whatever is chosen.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerOpenToExample : Component
{
private static readonly DateOnly Today = DateOnly.FromDateTime(TimeProvider.System.GetLocalNow().Date);
// The first of next month, a year on.
private static readonly DateOnly NextYear = new DateOnly(Today.Year, Today.Month, 1).AddMonths(1).AddYears(1);
protected override Component? Render() =>
Div.Class("max-w-xs space-y-4")[
Ui.DatePicker.Value(default(DateOnly)).OpenTo(NextYear),
Ui.DatePicker.Value(Today).OpenTo(NextYear).ForceOpenTo()
];
}Week numbers
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerWeekNumbersExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.DatePicker.Value(default(DateOnly)).WeekNumbers()
];
}Localization
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerLocaleExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.DatePicker.Value(default(DateOnly)).Locale("ja-JP")
];
}Clearable
A button inside the trigger, before its chevron, while a date is chosen, beside a time picker that has one too.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class DatePickerClearableExample : Component
{
private DateOnly _day = DateOnly.FromDateTime(TimeProvider.System.GetLocalNow().Date);
private TimeOnly? _time = new TimeOnly(9, 30);
protected override Component? Render() =>
Div.Class("max-w-md space-y-3")[
Div.Class("flex flex-wrap gap-2").Data("testid", "ui-picker-clear")[
Ui.DatePicker.Value(_day).OnChange(day => { _day = day; }).Clearable().Class("grow"),
Ui.TimePicker.Value(_time).OnChange(time => { _time = time; }).Clearable().Id("ui-time-clear")
],
Ui.Text.Data("testid", "ui-picker-clear-state")[(_day == default ? "No day" : "A day") + ", " + (_time is null ? "no time" : "a time")]
];
}A day, a time
Behaviour
A popup the browser opens
The calendar is in a popover, so the top layer, Escape, a click outside and the focus back on the button come with it. A day closes it on the pick, a range on its second click, a preset at once. While it is open the page behind it neither scrolls nor takes the pointer (data-rask-lock).
It takes the calendar's steps (Min, Max, Unavailable, MinRange, MaxRange, Months, OpenTo, StartDay, WeekNumbers, SelectableHeader, WithToday, FixedWeeks, Locale) and a field's. WithConfirmation() writes nothing until "Select date". There is no multiple mode, as in Flux: several days are the calendar's.
On a phone
Under 640 px the same popup is Flux's sheet, with nothing to write for it: centred over a page dimmed 10%, one month whatever Months says, 44 px days, the presets a native select under the days, and Cancel and Select date (Select range on a range) at its foot. A pick marks the day and writes nothing; the button writes it, through the same binding. Cancel, Escape and a press on the dimmed page leave the value as it was. The page hears which of the two it is from a box only the stylesheet sizes (data-rask-measure).
Typed dates
Type(Ui.DatePickerType.Input), Ui.DatePickerInput and WithInputs() draw month, day and year as parts in the locale's order, Flux's custom. Ui.DatePickerInput.Native and WithInputs(Ui.DatePickerInputVariant.Native) draw the browser's own <input type="date"> in the same box, Flux's native. Flux's docs default to native and call custom the recommended and future default; its own pages draw custom, and so does the kit unless told.
The typed trigger is one field in three parts: digits only, a part that can take no more moves on (3 is March), the horizontal arrows walk the parts and the vertical ones step them, and a pasted date is shared out. A whole date is written as soon as the year has four digits. Everything in the trigger but a part opens the calendar. The parts are the runtime's data-rask-segments, and one hidden field carries yyyy-MM-dd to the binding.
Where it departs from Flux, by the owner's decision: Flux keeps the last whole date behind parts that no longer show it. Here a half-filled date is no date: the field says "Enter a valid date.", the save is refused, and with every part emptied the picker holds nothing.
Presets
WithPresets() lists Flux's default seven; Presets([..]) lists the Ui.DateRangePresets you name, in your order. A preset writes UiDateRange.Of(preset, today, startDay, Min), whose Preset names it, and the button then shows the preset's label. All Time starts at Min. The presets are a radio group one arrow walks, and the preset an arrow reaches is chosen, which closes the picker.
Clearing
Clearable(), on the picker or on a Ui.DatePickerButton or Ui.DatePickerInput in its Trigger, puts a button in the trigger, before its chevron, while a date is chosen. A bound DateOnly? or UiDateRange? becomes null, one that cannot be null goes back to its unset value, and a range is emptied whole. Flux documents the prop and draws it on none of its pages, so the button is the time picker's: the same 32 px cross, 30 px from the trigger's end.
The keyboard
As measured on Flux. Inside the popup the calendar's own keys apply.
Key | What happens |
|---|---|
| Enter, Space, ArrowDown, ArrowUp | Open the closed button. |
| Escape, a click outside | Close; the focus goes back to the button. |
| An arrow in the presets | Chooses the next preset, which closes the picker. |
| Digits in a typed part | Fill it, and move on once it can take no more. |
Reference
Ui.DatePicker
A field-shaped button over a calendar in a popup. Ui.DatePicker picks a day, Ui.DatePicker.Range a range.
Step | Takes | What it does |
|---|---|---|
.Bind(…) | () => a DateOnly, or a UiDateRange | The member it reads and writes: () => m.Day, or a UiDateRange. Or Value with OnChange. |
.Value(…) | a DateOnly, or a UiDateRange | 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 picked. |
.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 picked, not with the next action. |
.Mode(…) | Ui.DatePickerModeSingle 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. |
.Type(…) | Ui.DatePickerTypeButton Input Each is a step of its own too, unless an HTML tag has its name. | What opens it: a button showing the choice, or a field to type it into. The button when unset. |
.Trigger(…) | Component | A trigger of your own: Ui.DatePickerInput fields, or a Ui.DatePickerButton. |
.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 trigger shows while nothing is chosen. By mode when unset. |
.Min(…) | a DateOnly | The earliest day that can be picked; also where All Time starts. |
.Max(…) | a DateOnly | The latest day that can be picked. |
.Unavailable(…) | DateOnly days: [today.AddDays(1)] | Days that cannot be picked. |
.MinRange(…) | int | The fewest days a range may hold. |
.MaxRange(…) | int | The most days a range may hold. |
.OpenTo(…) | a DateOnly | The day the calendar opens to while nothing is chosen. |
.ForceOpenTo() | bool | Opens to OpenTo whatever is chosen. |
.Months(…) | int | How many months are shown side by side. One, or two for a range, when unset. |
.Size(…) | Ui.DatePickerSizeDefault Sm Lg Xl Xxl Each is a step of its own too, unless an HTML tag has its name. | How large 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. |
.WithInputs(…) | Ui.DatePickerInputVariantCustom Native Each is a step of its own too, unless an HTML tag has its name. | Fields over the months to type into: WithInputs() as parts, WithInputs(Ui.DatePickerInputVariant.Native) as the browser's. |
.WithConfirmation() | bool | Holds the choice until the reader presses Select date. |
.WithPresets() | bool | Lists Flux's seven preset ranges beside the calendar. |
.Presets(…) | Ui.DateRangePreset values: [Ui.DateRangePreset.Today] | The presets listed, in this order. Setting it lists them. |
.Clearable() | bool | A button in the trigger that empties the picker, while a date is chosen. |
.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 and the shown date follow. The app's culture when unset. |
.ShowValidation() | bool | False leaves the member's message to a Ui.Error placed elsewhere. |
Ui.DatePickerInput
A field in a picker's Trigger to type its date into, which also opens the calendar. Native is the browser's own date field.
Step | Takes | What it does |
|---|---|---|
.Label(…) | string | The label over the field, which wraps it in a field. |
.Description(…) | string | Help text between the label and the field. |
.Variant(…) | Ui.DatePickerInputVariantCustom Native Each is a step of its own too, unless an HTML tag has its name. | Typed as parts, or in the browser's own date field. Parts when unset. |
.Size(…) | Ui.DatePickerInputSizeBase Sm Xs Each is a step of its own too, unless an HTML tag has its name. | How tall the field is. |
.Clearable() | bool | A button that empties the picker, while a date is chosen. The picker's own when unset. |
.Disabled() | bool | A field that cannot be typed into or open the picker. |
.Invalid() | bool | Draws the field as holding an error. |
Ui.DatePickerButton
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.DatePickerButtonSizeBase 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 a date is chosen. The picker's own when unset. |
.Disabled() | bool | A button that cannot open the picker. |
.Invalid() | bool | Draws the button as holding an error. |