Browse docs
Browse docs
TimePicker is a form-connected time-of-day field: a read-only trigger paired
with a time-list popover. Pick a slot from the list. It integrates with
DashForm and RBAC automatically — the name prop connects the field to the
form schema.
The stored value is a canonical 24-hour "HH:mm" string, or null. 12-hour
notation is a display concern (hour12) — never the storage format. The field
carries no date and no time zone.
import { DashForm } from '@dashforge/forms';
import { TimePicker } from '@dashforge/tw';
<DashForm onSubmit={(data) => console.log(data)}>
<TimePicker
name="meetingTime"
label="Meeting time"
rules={{ required: 'Please pick a time' }}
/>
</DashForm>A basic field — click the trigger and pick a time from the popover.
import { TimePicker } from '@dashforge/tw';
<TimePicker name="meetingTime" label="Meeting time" />minTime / maxTime bound the selectable range; stepMinutes sets the
list granularity.
09:00–17:00, 15-minute steps
import { TimePicker } from '@dashforge/tw';
<TimePicker
name="slot"
label="Appointment slot"
minTime="09:00"
maxTime="17:00"
stepMinutes={15}
helperText="09:00–17:00, 15-minute steps"
/>hour12 renders the trigger and list in 12-hour notation. The stored value
stays the canonical 24-hour "HH:mm".
import { TimePicker } from '@dashforge/tw';
<TimePicker name="alarm" label="Alarm" hour12 defaultValue="14:30" />Configure <TimePicker> defaults application-wide.
import { patchTheme } from '@dashforge/tw-theme';
patchTheme({
components: {
TimePicker: {
defaults: { layout: 'stacked', stepMinutes: 15, hour12: false, fullWidth: false },
},
},
});Configurable axes (TimePickerVariantProps):
| Axis | Type | Notes |
|---|---|---|
layout | 'stacked' | 'inline' | Label position (above vs left of the trigger). |
stepMinutes | number | Step between list options — 15, 30 (default), 60. |
hour12 | boolean | Render trigger + list in 12-hour notation. Stored value stays 24-hour "HH:mm". |
fullWidth | boolean | Stretch root + trigger to container width. |
Non-visual axes (name, rules, label, helperText, placeholder, required, error, disabled, value, defaultValue, onChange, minTime, maxTime, access, visibleWhen) are per-instance.
Precedence chain (lowest → highest):
layout: 'stacked', stepMinutes: 30, hour12: false, fullWidth: false).theme.components.TimePicker.defaults (application-wide).<TimePicker stepMinutes={15} hour12 />) — wins over theme.sx — appended to the root and wins over conflicting classes via tailwind-merge.TypeScript: theme.components.TimePicker.defaults autocompletes to the four axes above.
Reactivity: useComponentDefaults('TimePicker') 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. |
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. |
label | ReactNode | — | Field label. |
layout | 'stacked' | 'inline' | — | Label/control layout. |
maxTime | string | — | Latest selectable time, "HH:mm". Default "23:30". |
minTime | string | — | Earliest selectable time, "HH:mm". Default "00:00". |
onChange | (value: string | null) => void | — | Fired with the new time (or null when cleared). |
placeholder | string | — | Placeholder shown when no time is selected. |
required | boolean | — | Marks the field required (adds the label asterisk). |
rules | unknown | — | Validation rules forwarded to the form bridge. |
slotProps | TimePickerSlotProps | — | 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 — "HH:mm" (24-hour) or null. |
visibleWhen | (engine: Engine) => boolean | — | Reactive visibility predicate evaluated against the form engine. |
<TimePicker> 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 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 (e.g. trigger border, errorText font-weight).
Example:
<TimePicker name="meetingTime" label="Meeting time" sx="max-w-xs" />"HH:mm" storage. The bridge value is always the canonical
24-hour form, even when hour12 is set.DatePicker; for date +
time in one control use DateTimePicker.<TimePicker value={time} onChange={(v) => setTime(v)} label="Meeting time" />Inside DashForm the field registers automatically — validation, error
gating, and touch tracking flow through the bridge.
<TimePicker
name="cutoffTime"
label="Cut-off time"
access={{ resource: 'schedule', action: 'update', onUnauthorized: 'disable' }}
/>