Browse docs
Browse docs
DateTimePicker is a form-connected date and time field: a read-only
trigger that opens a popover combining a Calendar
with a time list. Pick a date, then a time. It integrates with DashForm and
RBAC automatically.
The stored value is a naive ISO datetime — "YYYY-MM-DDTHH:mm" — or null.
No seconds, no time zone.
import { DashForm } from '@dashforge/forms';
import { DateTimePicker } from '@dashforge/tw';
<DashForm onSubmit={(data) => console.log(data)}>
<DateTimePicker
name="appointment"
label="Appointment"
rules={{ required: 'Please pick a date and time' }}
/>
</DashForm>The committed value is a string: "2026-05-15T10:00".
A basic field — open the popover, click a date (the popover stays open), then a time (the popover closes).
import { DateTimePicker } from '@dashforge/tw';
<DateTimePicker name="appointment" label="Appointment" />minDate / maxDate bound the calendar; stepMinutes sets the time-list
granularity.
import { DateTimePicker } from '@dashforge/tw';
<DateTimePicker
name="slot"
label="Appointment slot"
minDate="2026-05-01"
maxDate="2026-05-31"
stepMinutes={30}
defaultValue="2026-05-15T10:00"
/>Configure <DateTimePicker> defaults application-wide.
import { patchTheme } from '@dashforge/tw-theme';
patchTheme({
components: {
DateTimePicker: {
defaults: {
layout: 'stacked',
weekStartDay: 1,
locale: 'en-GB',
stepMinutes: 15,
hour12: false,
fullWidth: false,
},
},
},
});Configurable axes (DateTimePickerVariantProps):
| Axis | Type | Notes |
|---|---|---|
layout | 'stacked' | 'inline' | Label position (above vs left of the trigger). |
weekStartDay | WeekDay (0 | 1 | … | 6) | First-column weekday of the popover calendar. |
locale | string | BCP-47 locale for the calendar + display format. |
stepMinutes | number | Step between time-list options — 15, 30 (default), 60. |
hour12 | boolean | Render time list in 12-hour notation. Stored value stays 24-hour ISO. |
fullWidth | boolean | Stretch root + trigger to container width. |
Non-visual axes (name, rules, label, helperText, placeholder, required, error, disabled, value, defaultValue, onChange, minDate, maxDate, disabledDates, isDateDisabled, access, visibleWhen) are per-instance.
Precedence chain (lowest → highest):
layout: 'stacked', weekStartDay: 0, locale: 'en-US', stepMinutes: 30, hour12: false, fullWidth: false).theme.components.DateTimePicker.defaults (application-wide).sx — appended to the root and wins over conflicting classes via tailwind-merge.TypeScript: theme.components.DateTimePicker.defaults autocompletes to the six axes above.
Reactivity: useComponentDefaults('DateTimePicker') subscribes to the theme store — patchTheme re-renders every mounted instance inheriting the changed axis.
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | — | Field name — the bridge registration key. Required. |
access | AccessRequirement | — | RBAC access requirement. |
defaultValue | string | null | — | Uncontrolled initial value. |
disabled | boolean | — | Disables the field. |
disabledDates | readonly ISODate[] | — | Explicit list of disabled dates. |
error | boolean | — | Explicit error state (overrides the bridge's auto error). |
fullWidth | boolean | — | Stretches the field to its container width. |
helperText | ReactNode | — | Helper text below the control (overrides a bridge error message). |
hour12 | boolean | — | Render the time list in 12-hour notation. Default false. |
isDateDisabled | (date: ISODate) => boolean | — | Predicate marking arbitrary dates disabled. |
label | ReactNode | — | Field label. |
layout | 'stacked' | 'inline' | — | Label/control layout. |
locale | string | — | BCP-47 locale for the calendar and the display format. |
maxDate | ISODate | — | Latest selectable date (inclusive). |
minDate | ISODate | — | Earliest selectable date (inclusive). |
onChange | (value: string | null) => void | — | Fired with the new datetime (or null when cleared). |
placeholder | string | — | Placeholder shown when no value is selected. |
required | boolean | — | Marks the field required (adds the label asterisk). |
rules | unknown | — | Validation rules forwarded to the form bridge. |
slotProps | DateTimePickerSlotProps | — | Per-slot className overrides. |
stepMinutes | number | — | Step between time-list options, in minutes. Default 30. |
sx | string | — | Root-level Tailwind class override. |
testId | string | — | Test id applied to the field root. |
value | string | null | — | Controlled value — "YYYY-MM-DDTHH:mm" or null. |
visibleWhen | (engine: Engine) => boolean | — | Reactive visibility predicate evaluated against the form engine. |
weekStartDay | WeekDay | — | Weekday of the calendar's first column (0 = Sunday). |
<DateTimePicker> is a compound component with 6 named slots. Each slot accepts a { className?: string } override via slotProps:
| Slot | Purpose |
|---|---|
root | Outer wrapper (label + trigger + helper/error). |
label | The <label> element. |
requiredMark | The red * when required. |
trigger | The read-only <button> that opens the combined calendar + time-list popover. |
helperText | Helper text line, when not in error state. |
errorText | Helper text line, when in error state. |
Use sx for a root-level class override; use slotProps for a specific inner region. The popover calendar inherits theme.components.Calendar.defaults for its day-cell surface.
"YYYY-MM-DDTHH:mm" storage — no seconds, no time zone.0.10.0-beta): the legacy native-input DateTimePicker
is gone. The mode prop is removed — use DatePicker / TimePicker for
date-only / time-only; min / max / step / onValueChange are now
minDate / maxDate / stepMinutes / onChange. The isoToInputValue
export was also removed.<DateTimePicker
value={appointment}
onChange={(v) => setAppointment(v)}
label="Appointment"
/>Inside DashForm the field registers automatically — validation, error
gating, and touch tracking flow through the bridge.
<DateTimePicker
name="publishAt"
label="Publish at"
access={{ resource: 'article', action: 'publish', onUnauthorized: 'hide' }}
/>