Skip to main content

DateTimeInput

Overview​

The DateTimeInput combines a calendar and a time wheel spinner in a single popover — an iOS-style layout.

The popover opens to the calendar with an iOS-style footer: the selected time as a tappable value, a Reset button (reverts your edits to the value the popover opened with), and a circular ✓ confirm button. Clicking the time floats the wheel spinner over the calendar (hours, minutes, and optionally seconds — the same wheels the TimeInput uses), so the popover never grows or scrolls; clicking the time again, clicking outside the wheel card, or pressing Escape hides it. It supports the full prop surface of both DateInput and TimeInput, plus a native <input type="datetime-local"> fallback for touch devices. A clickable launcher icon on the right opens the popover, and you can type directly in the field with segmented keyboard entry.


Import​

import { DateTimeInput } from '@allxsmith/bestax-bulma';

Usage​

Basic DateTimeInput​

The popover opens to the calendar with an iOS-style footer — the selected time, a Reset button, and a circular ✓ to confirm. Click the time value to float the wheel spinner over the calendar.

<DateTimeInput label="Appointment" placeholder="YYYY-MM-DD HH:MM" />

Typing-first — the same example with openOnFocus={false}: focusing or clicking the field lets you type; open the popover with the launcher icon (or press ↓).

<DateTimeInput
  label="Appointment"
  placeholder="YYYY-MM-DD HH:MM"
  openOnFocus={false}
/>


Controlled​

function example() {
  const [v, setV] = useState(new Date());
  return (
    <Block>
      <DateTimeInput label="Meeting" value={v} onChange={setV} />
      <Paragraph mt="2">Selected: {v ? v.toString() : '—'}</Paragraph>
    </Block>
  );
}

Typing-first — the same controlled example with openOnFocus={false} added: click in and type freely, and reach for the launcher icon (or ↓) when you want the popover.

function example() {
  const [v, setV] = useState(new Date());
  return (
    <Block>
      <DateTimeInput
        label="Meeting"
        value={v}
        onChange={setV}
        openOnFocus={false}
      />
      <Paragraph mt="2">Selected: {v ? v.toString() : '—'}</Paragraph>
    </Block>
  );
}


12-hour Format​

<DateTimeInput
  label="12-hour"
  hourFormat="12"
  defaultValue={new Date()}
  mobileNative={false}
/>

Forced to the custom popover

The OS-native pickers use the device clock setting, so hourFormat is ignored there. This example forces mobileNative={false} so the 12-hour format shows on touch devices too.

Typing-first — the same 12-hour field with openOnFocus={false}: type across the segments (press a / p on the trailing meridiem) and open the popover with the launcher icon or ↓.

function example() {
  return (
    <DateTimeInput
      label="12-hour entry"
      hourFormat="12"
      defaultValue={new Date(2024, 5, 7, 13, 45)}
      mobileNative={false}
      openOnFocus={false}
    />
  );
}


With Seconds​

<DateTimeInput
  label="With seconds"
  enableSeconds
  defaultValue={new Date()}
  mobileNative={false}
/>

iOS Safari has no seconds wheel (Android Chrome shows one with caveats)

This example forces mobileNative={false} so the seconds wheel shows on every device. Android Chrome generally renders a seconds component in its native datetime-local picker when step < 60 (with some long-standing quirks — exact seconds-spinner behavior varies by Android version). iOS Safari has no seconds wheel under any circumstances. If you need a guaranteed seconds wheel, pass mobileNative={false} to force the custom wheel popover. See Mobile Native below for the full iOS-vs-Android picker support matrix.

Typing-first — the same seconds-enabled field with openOnFocus={false}: the typed walk gains a seconds segment (year → month → day → hours → minutes → seconds), and the launcher icon (or ↓) opens the popover.

function example() {
  return (
    <DateTimeInput
      label="With a seconds segment"
      enableSeconds
      defaultValue={new Date(2024, 5, 7, 13, 45, 30)}
      mobileNative={false}
      openOnFocus={false}
    />
  );
}


12-hour with Seconds​

Combine hourFormat="12" with enableSeconds for an hh:mm:ss A field — the wheel gains hours, minutes, seconds, and AM/PM columns.

<DateTimeInput
  label="12-hour with seconds"
  hourFormat="12"
  enableSeconds
  defaultValue={new Date()}
  mobileNative={false}
/>

Forced to the custom popover

The OS-native pickers honor neither half: hourFormat follows the device clock setting, and iOS Safari has no seconds wheel at all. This example forces mobileNative={false} so the 12-hour seconds wheel shows on every device.

Typing-first — the full segment set with openOnFocus={false}: type through date, hh, mm, ss, toggle the trailing meridiem with a / p, and open the popover via the launcher icon or ↓.

function example() {
  return (
    <DateTimeInput
      label="12-hour with seconds and AM/PM"
      hourFormat="12"
      enableSeconds
      defaultValue={new Date(2024, 5, 7, 13, 45, 30)}
      mobileNative={false}
      openOnFocus={false}
    />
  );
}


Formats​

The format prop takes a token string or Intl.DateTimeFormatOptions spanning the whole date-time. Padded token formats keep the field segmented-typeable; Intl formats are display-only unless you add a custom parse.

<Block display="flex" flexDirection="column" gap="4">
  <DateTimeInput
    label="YYYY-MM-DD HH:mm (default)"
    defaultValue={new Date(2026, 4, 30, 13, 45)}
    mobileNative={false}
  />
  <DateTimeInput
    label="MM/DD/YYYY hh:mm A"
    format="MM/DD/YYYY hh:mm A"
    defaultValue={new Date(2026, 4, 30, 13, 45)}
    mobileNative={false}
  />
  <DateTimeInput
    label="DD.MM.YYYY HH:mm"
    format="DD.MM.YYYY HH:mm"
    defaultValue={new Date(2026, 4, 30, 13, 45)}
    mobileNative={false}
  />
  <DateTimeInput
    label="Intl — display only"
    format={{ dateStyle: 'medium', timeStyle: 'short' }}
    editable={false}
    defaultValue={new Date(2026, 4, 30, 13, 45)}
    mobileNative={false}
  />
</Block>

Forced to the custom popover

format is ignored by the OS-native pickers (they use the device locale), so these examples set mobileNative={false} to show the formats on touch devices too.

Typing-first — a custom DD.MM.YYYY HH:mm format with openOnFocus={false}: segments follow the format order (day first) and typing ., space, or : jumps the separators — so 25.12.2026 09:30 flows straight through — with the launcher icon (or ↓) opening the popover.

function example() {
  return (
    <DateTimeInput
      label="DD.MM.YYYY HH:mm with dot separators"
      format="DD.MM.YYYY HH:mm"
      defaultValue={new Date(2024, 5, 7, 13, 45)}
      mobileNative={false}
      openOnFocus={false}
    />
  );
}


Launcher Icon​

A clickable launcher sits on the right and toggles the popover — handy for input-mode (openOnFocus={false}) where you type the value and click the icon to open the picker. Override its glyph with triggerIconName, or hide it with triggerIcon={false} (the popover still opens on focus / click). The decorative left icon is independent: it shows by default, takes its glyph from iconLeftName, and is hidden with iconLeftName="".

<Block display="flex" flexDirection="column" gap="4">
  <DateTimeInput label="Default (left icon + right launcher)" />
  <DateTimeInput
    label="Custom launcher glyph"
    triggerIconName="calendar-check"
  />
  <DateTimeInput label="No launcher" triggerIcon={false} />
  <DateTimeInput label="Left icon hidden" iconLeftName="" />
</Block>

Typing-first — the same group with openOnFocus={false} on every instance, so clicking a field just lets you type and the launcher icon opens the popover; note the triggerIcon={false} instance has no launcher, leaving its popover keyboard-only via ↓.

<Block display="flex" flexDirection="column" gap="4">
  <DateTimeInput
    label="Default (left icon + right launcher)"
    openOnFocus={false}
  />
  <DateTimeInput
    label="Custom launcher glyph"
    triggerIconName="calendar-check"
    openOnFocus={false}
  />
  <DateTimeInput
    label="No launcher (popover via ↓ only)"
    triggerIcon={false}
    openOnFocus={false}
  />
  <DateTimeInput label="Left icon hidden" iconLeftName="" openOnFocus={false} />
</Block>


Min and Max​

Bounds apply to the combined date-time.

function example() {
  const today = new Date();
  const min = new Date(today);
  min.setHours(9, 0, 0, 0);
  const max = new Date(today);
  max.setHours(17, 0, 0, 0);
  return <DateTimeInput label="Office hours today" min={min} max={max} />;
}

iOS native does not enforce min/max in the picker

On iOS Safari the picker UI lets the user pick any value; min/max only fire at form-submission validation (WebKit bug #225639, still open). Pass mobileNative={false} for iOS-side enforcement. Android Chrome's native picker does honor them.

Typing-first — the same bounds with openOnFocus={false}: keystrokes and ↑ / ↓ arrows never produce a value outside min/max, and the launcher icon (or ↓) opens the popover.

function example() {
  const now = new Date();
  const min = new Date(now);
  min.setHours(9, 0, 0, 0);
  const max = new Date(now);
  max.setHours(17, 0, 0, 0);
  const noon = new Date(now);
  noon.setHours(12, 0, 0, 0);
  return (
    <DateTimeInput
      label="Office hours today only — typed entry too"
      min={min}
      max={max}
      defaultValue={noon}
      openOnFocus={false}
    />
  );
}


Disabled Dates​

Blocked dates are disabled in the calendar and rejected during manual typing, the same way min/max are enforced.

<DateTimeInput
  label="No weekend appointments"
  shouldDisableDate={d => d.getDay() === 0 || d.getDay() === 6}
  mobileNative={false}
/>

Forced to the custom popover

HTML has no predicate equivalent, so the OS-native pickers can't block any dates. This example forces mobileNative={false} so the rule works on touch devices; in your app keep mobileNative="auto" and also validate in onChange.

Typing-first — the same predicate with openOnFocus={false}: a keystroke or arrow that lands on a blocked date is rejected (matching the disabled calendar cells), and the launcher icon (or ↓) opens the popover.

function example() {
  return (
    <DateTimeInput
      label="Weekends rejected while typing"
      shouldDisableDate={d => d.getDay() === 0 || d.getDay() === 6}
      defaultValue={new Date(2024, 5, 7, 13, 45)}
      mobileNative={false}
      openOnFocus={false}
    />
  );
}


Unselectable Times​

Blocked times are skipped by the wheels and rejected during manual typing.

<DateTimeInput
  label="Lunch hour blocked"
  unselectableTimes={d => d.getHours() === 12}
  defaultValue={new Date()}
  mobileNative={false}
/>

Forced to the custom popover

Same as Disabled Dates — the OS-native pickers can't evaluate predicates. This example forces mobileNative={false} so the blocked hour works on touch devices too.

Typing-first — the same blocked hour with openOnFocus={false}: setting the hour segment to a blocked hour is vetoed by the unselectableTimes predicate (just as the wheels skip it), and the launcher icon (or ↓) opens the popover.

function example() {
  return (
    <DateTimeInput
      label="Lunch hour rejected while typing"
      unselectableTimes={d => d.getHours() === 12}
      defaultValue={new Date(2024, 5, 7, 11, 30)}
      mobileNative={false}
      openOnFocus={false}
    />
  );
}


Inline​

<DateTimeInput label="Inline" inline defaultValue={new Date()} />


First Day of Week​

<DateTimeInput
  label="Monday-first"
  firstDayOfWeek={1}
  defaultValue={new Date()}
  mobileNative={false}
/>

Forced to the custom popover

The OS-native calendars use the device locale for the week start, so firstDayOfWeek is ignored there. This example forces mobileNative={false} so the Monday-first grid shows on touch devices too.

Typing-first — the same Monday-first example with openOnFocus={false}: type in the field directly and bring up the popover with the launcher icon (or ↓) to see the week start.

<DateTimeInput
  label="Monday-first"
  firstDayOfWeek={1}
  defaultValue={new Date()}
  mobileNative={false}
  openOnFocus={false}
/>


Mobile Native​

By default mobileNative='auto': on touch devices with a small viewport ((pointer: coarse) and (max-width: 768px)) the input swaps to a plain <input type="datetime-local"> so the OS-native picker handles the interaction. Pass true or false to override.

<DateTimeInput label="Native datetime-local" mobileNative={true} />

Native picker support varies — iOS lags Android

The OS-native fallback is just a <input type="datetime-local">, so it inherits each platform's behavior. The custom popover (mobileNative={false}) honors every prop on every device.

Honored on Android Chrome but NOT on iOS Safari:

  • min / max — Android Chrome dims out-of-range values in the picker; iOS lets the user pick any value, only firing the constraint at form-submission validation. (WebKit bug #225639, still open as of 2026.)
  • incrementMinutes, incrementHours — Android Chrome respects step (e.g. only 0/15/30/45 minutes selectable when step=900). iOS shows every value regardless.
  • enableSeconds — Android Chrome shows a seconds component when step < 60 (with some long-standing quirks for datetime-local). iOS has no seconds wheel under any circumstances.

Ignored on BOTH iOS Safari and Android Chrome (HTML-spec gaps):

  • shouldDisableDate, unselectableDates, unselectableTimes — HTML has no predicate/array equivalent; native pickers can't evaluate functions.
  • firstDayOfWeek, dayNames, monthNames, nearbyMonthDays — both use the device's system locale.
  • hourFormat — both use the device's system clock setting (12h/24h).
  • format, locale — both use the device's system locale; per-input overrides are ignored.
  • placeholder — neither renders placeholder text.

If any of these matter, pass mobileNative={false} to force the custom popover (works on every device), or duplicate the constraint in onChange / server-side validation.


Locale​

<Block display="flex" flexDirection="column" gap="4">
  <DateTimeInput
    label="ja-JP"
    locale="ja-JP"
    defaultValue={new Date()}
    mobileNative={false}
  />
  <DateTimeInput
    label="fr-FR"
    locale="fr-FR"
    defaultValue={new Date()}
    mobileNative={false}
  />
</Block>

Forced to the custom popover

The OS-native pickers always use the device's system locale, so these examples set mobileNative={false} to show the per-input locale on touch devices too.

Typing-first — the same locales with openOnFocus={false} on each instance: focus to type the localized value, and use the launcher icon (or ↓) to open the popover.

<Block display="flex" flexDirection="column" gap="4">
  <DateTimeInput
    label="ja-JP"
    locale="ja-JP"
    defaultValue={new Date()}
    mobileNative={false}
    openOnFocus={false}
  />
  <DateTimeInput
    label="fr-FR"
    locale="fr-FR"
    defaultValue={new Date()}
    mobileNative={false}
    openOnFocus={false}
  />
</Block>


Sizes​

<Block display="flex" flexDirection="column" gap="4">
  <DateTimeInput label="Small" controlSize="small" size="small" />
  <DateTimeInput label="Default" />
  <DateTimeInput label="Medium" controlSize="medium" size="medium" />
  <DateTimeInput label="Large" controlSize="large" size="large" />
</Block>

Typing-first — every size with openOnFocus={false}: clicking any field lets you type straight away, with the launcher icon (or ↓) opening the popover.

<Block display="flex" flexDirection="column" gap="4">
  <DateTimeInput
    label="Small"
    controlSize="small"
    size="small"
    openOnFocus={false}
  />
  <DateTimeInput label="Default" openOnFocus={false} />
  <DateTimeInput
    label="Medium"
    controlSize="medium"
    size="medium"
    openOnFocus={false}
  />
  <DateTimeInput
    label="Large"
    controlSize="large"
    size="large"
    openOnFocus={false}
  />
</Block>


Colors​

<Block display="flex" flexDirection="column" gap="4">
  <DateTimeInput label="Primary" color="primary" />
  <DateTimeInput label="Info" color="info" />
  <DateTimeInput label="Success" color="success" />
  <DateTimeInput label="Warning" color="warning" />
  <DateTimeInput label="Danger" color="danger" />
</Block>

Typing-first — the same colors with openOnFocus={false} on every instance: type directly in any field and open the popover with the launcher icon (or ↓).

<Block display="flex" flexDirection="column" gap="4">
  <DateTimeInput label="Primary" color="primary" openOnFocus={false} />
  <DateTimeInput label="Info" color="info" openOnFocus={false} />
  <DateTimeInput label="Success" color="success" openOnFocus={false} />
  <DateTimeInput label="Warning" color="warning" openOnFocus={false} />
  <DateTimeInput label="Danger" color="danger" openOnFocus={false} />
</Block>


States​

<Block display="flex" flexDirection="column" gap="4">
  <DateTimeInput label="Disabled" disabled />
  <DateTimeInput label="Read only" readOnly defaultValue={new Date()} />
  <DateTimeInput label="Loading" isLoading />
</Block>


Context-Aware Rendering​

Default (with label)​

<DateTimeInput label="When" placeholder="YYYY-MM-DD HH:MM" />


With Field Wrapper​

function example() {
  return (
    <Field horizontal label="When">
      <Field.Body>
        <Field>
          <DateTimeInput placeholder="YYYY-MM-DD HH:MM" />
        </Field>
      </Field.Body>
    </Field>
  );
}


With Field and Control Wrappers​

function example() {
  return (
    <Field horizontal label="When">
      <Field.Body>
        <Field>
          <Control iconLeftName="calendar-alt">
            <DateTimeInput placeholder="YYYY-MM-DD HH:MM" />
          </Control>
        </Field>
      </Field.Body>
    </Field>
  );
}


Manual Keyboard Entry​

The single input spans the whole date-time: year → month → day → hours → minutes (plus seconds / AM-PM when enabled). Focus highlights the year; ↑ / ↓ adjust a segment, → / ← move between them, digits overwrite with auto-advance, and typing a -, space, or : jumps across the separators. Segment mode activates whenever format is a token string with padded tokens (YYYY, MM, DD, HH/hh, mm, ss, A); Intl.DateTimeFormatOptions formats and single-character tokens fall back to free-form text.

These examples use openOnFocus={false} so the popover doesn't cover the input.

Opening the picker vs. typing

With openOnFocus={false} (used here), clicking the field just lets you type — the popover does not appear on focus or click. Open the picker by clicking the launcher icon on the right (or pressing ↓). With the default openOnFocus={true}, focusing or clicking the field opens the popover immediately (you can still type while it's open).

Basic​

function example() {
  return (
    <DateTimeInput
      label="Type across date and time"
      defaultValue={new Date(2024, 5, 7, 13, 45)}
      openOnFocus={false}
    />
  );
}


Controlled with live value​

function example() {
  const [v, setV] = useState(() => new Date(2024, 5, 7, 13, 45));
  return (
    <Block>
      <DateTimeInput
        label="Arrow or type — value updates live"
        value={v}
        onChange={setV}
        openOnFocus={false}
      />
      <Paragraph mt="2">Selected: {v ? v.toString() : '—'}</Paragraph>
    </Block>
  );
}


Free-form fallback​

An Intl.DateTimeFormatOptions format has no segment map, so entry is free-form — focusing does not highlight a segment and the text parses on Enter or blur instead.

function example() {
  return (
    <DateTimeInput
      label="Free-form (Intl format)"
      format={{ dateStyle: 'medium', timeStyle: 'short' }}
      defaultValue={new Date(2024, 5, 7, 13, 45)}
      openOnFocus={false}
    />
  );
}


Picker vs Input Modes​

editable controls whether segmented typing is allowed; popover controls whether the calendar + time panel exists. Both default to true.

editablepopoverBehavior
truetrueBoth — segmented typing + popover (default)
falsetruePicker-only — typing inert, popover opens
truefalseInput-only — segmented typing, no popover
falsefalseStatic display

Picker only​

<DateTimeInput label="Picker only" editable={false} defaultValue={new Date()} />

Input only​

<DateTimeInput label="Input only" popover={false} defaultValue={new Date()} />


Keyboard Navigation​

On the input (segmented entry)​

A single field spans year → month → day → hours → minutes (→ seconds → AM/PM when enabled). Focus highlights the year; segment mode activates whenever format is a token string with padded tokens (YYYY, MM, DD, HH, hh, mm, ss, A); Intl formats and single-character tokens fall back to free-form text entry.

KeyAction
↑ / ↓Increment / decrement the active segment (wraps in place)
← / →Move to previous / next segment
0–9Overwrite the active segment; auto-advances when no further digit is valid
a / A / p / PToggle AM/PM on the meridiem segment (12-hour formats)
Separator (- / : . space)Skip to the next segment without inserting the character
BackspaceClear the typed-digit buffer; if already cleared, move to the previous segment
TabClear segment selection so focus moves out naturally
EscapeClose the popover

On the popover​

KeyAction
↓Open popover (when closed)
EscapeClose popover
← / →Move focused date by ±1 day
↑ / ↓Move focused date by ±7 days
PageUp / PageDownMove focused date by ±1 month
Shift+PageUp/DownMove focused date by ±1 year
Home / EndJump to start / end of week
Enter / SpaceSelect focused date
TabMove focus from calendar → time button → footer

Activate the footer time button (Enter / Space) to float the wheels over the calendar. On a time wheel: ↑ / ↓ change the value, ← / → move between the hours / minutes / (seconds) columns, and Enter commits and closes. While the wheels are open, Escape collapses them (a second Escape closes the popover), and clicking anywhere outside the wheel card dismisses them.


Form Submission​

PropDescription
nameForm field name.
formOptional id of the form the input belongs to.
requiredMarks the field as required for native HTML form validation.
function DateTimeInputFormDemo() {
  const [submitted, setSubmitted] = React.useState('');
  return (
    <form
      onSubmit={e => {
        e.preventDefault();
        const fd = new FormData(e.currentTarget);
        setSubmitted(JSON.stringify(Array.from(fd.entries()), null, 2));
      }}
    >
      <DateTimeInput name="when" label="When" required />
      <div style={{ marginTop: '1rem' }}>
        <button type="submit" className="button is-primary">
          Submit
        </button>
      </div>
      {submitted && <pre style={{ marginTop: '1rem' }}>{submitted}</pre>}
    </form>
  );
}


Accessibility​

  • Trigger uses role="combobox" with aria-haspopup="dialog", aria-expanded, and aria-controls.
  • Popover panel has role="dialog" with an accessible name.
  • Calendar uses role="grid"; cells expose aria-selected, aria-disabled, and aria-current="date".
  • Each time wheel uses role="spinbutton" with aria-valuemin, aria-valuemax, aria-valuenow, and aria-valuetext.
  • The footer's confirm button exposes an accessible label (Done); the Reset button reverts your edits to the value the popover opened with.
  • Tab order naturally walks from calendar → time wheels → footer (Reset / ✓).


Additional Resources​

Pro Tip

Pair DateTimeInput with closeOnSelect={false} (the default) so users can tweak both date and time before committing via OK — closing on the first date click would surprise them.


Props​

The DateTimeInput prop set is the union of DateInput and TimeInput props. Notable additions and overrides:

PropTypeDefaultDescription
labelReact.ReactNode—Field label.
labelSize'small' | 'normal' | 'medium' | 'large'—Size for the label.
labelPropsReact.LabelHTMLAttributes<HTMLLabelElement> & { [key: string]: unknown; }—Props for the label element.
horizontalbooleanfalseRender the field with horizontal layout.
iconLeftIconProps—Icon props for the left icon.
iconRightIconProps—Icon props for the right icon.
iconRightNamestring—Shortcut for the right icon name.
iconLeftSize'small' | 'medium' | 'large'—Shortcut for left icon size.
iconRightSize'small' | 'medium' | 'large'—Shortcut for right icon size.
hasIconsLeftbooleanfalseForce the left icon container.
hasIconsRightbooleanfalseForce the right icon container.
isLoadingbooleanfalseShow a loading indicator on the control.
isExpandedbooleanfalseExpand the control to fill its container.
controlSize'small' | 'medium' | 'large'—Size of the wrapping Control.
messageReact.ReactNode—Help/validation text below the input.
messageColor'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger'—Message color.
fieldClassNamestring—Additional CSS classes for the Field wrapper.
controlClassNamestring—Additional CSS classes for the Control wrapper.
valueDate | null—Controlled selected date-time.
defaultValueDate | null—Initial value for uncontrolled usage.
onChange(d: Date | null) => void—Fired when either the date or time portion changes.
onOpen() => void—Fired when the popover opens.
onClose() => void—Fired when the popover closes.
minDate—Lower bound for the combined date-time.
maxDate—Upper bound for the combined date-time.
disabledbooleanfalseDisable the input.
readOnlybooleanfalseRead-only input.
placeholderstring—Placeholder text.
formatIntl.DateTimeFormatOptions | string'YYYY-MM-DD HH:mm'Token format string or Intl.DateTimeFormat options. Default 'YYYY-MM-DD HH:mm'.
parse(s: string) => Date | null—Custom parser.
localestring—BCP-47 locale tag.
inlinebooleanfalseRender the panel inline (no popover).
mobileNativeboolean | 'auto''auto'Use <input type="datetime-local"> on coarse-pointer devices.
editablebooleantrueAllow segmented keyboard typing (type the date-time directly across all segments). false makes the field picker-only.
popoverbooleantrueWhether the calendar + time popover exists. false makes the field input-only (segmented typing with no popover). Default true.
openOnFocusbooleantrueOpen the popover on focus. Default true.
closeOnSelectbooleanfalseOff by default — users typically tweak both halves before committing.
position'bottom-left' | 'bottom-right' | 'top-left' | 'top-right' | 'auto''bottom-left'Popover anchor position.
appendToBodybooleanfalseRender the popover into document.body via portal.
color'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger'—Bulma color modifier.
size'small' | 'medium' | 'large'—Size variant.
isRoundedbooleanfalseRounded input corners.
shouldDisableDate(d: Date) => boolean—Disable specific dates. Blocked dates are also rejected during manual typing (the predicate receives the full candidate date-time — prefer day-based checks).
unselectableDatesDate[]—Convenience array of disabled dates, matched by calendar day; also rejected by manual typing.
firstDayOfWeek0 | 1 | 2 | 3 | 4 | 5 | 60Day the calendar week starts on.
dayNamesstring[]—Override the 7 day-name labels.
monthNamesstring[]—Override the 12 month-name labels.
nearbyMonthDaysbooleantrueShow dimmed dates from adjacent months.
hourFormat'12' | '24''24'Time format.
enableSecondsbooleanfalseShow seconds column. Note: iOS Safari's native datetime-local picker UI does not include a seconds wheel; pass mobileNative={false} if you need one on iOS.
incrementHoursnumber1Hour step.
incrementMinutesnumber1Step between minute-wheel values (1 = every minute, iOS-style).
incrementSecondsnumber1Step between second-wheel values (only with enableSeconds).
unselectableTimes(d: Date) => boolean—Block specific times. Blocked times are also rejected during manual typing.
iconLeftNamestring'calendar-alt'Decorative left icon glyph for the wrapping Control (shown by default). Set '' to hide.
triggerIconbooleantrueShow a clickable launcher button on the right that toggles the popover. Default true.
triggerIconNamestring'chevron-down'Glyph for the right launcher button.
audioTickbooleanfalsePlay a short audible tick on each time-wheel crossing. Default false.
hapticsbooleanfalseAuto-route tactile feedback per wheel tick (vibrate on Android, audio thunk on iOS). Default false.
labelsPickerLabels—Optional translatable string overrides.
classNamestring—Additional CSS classes.
refReact.Ref<HTMLInputElement>—Forwarded to the underlying element.
namestring—Form field name.
formstring—Optional id of the form the input belongs to.
requiredbooleanfalseMarks the field as required for native HTML form validation.
...All standard <input> attributes and Bulma helper props—See Helper Props

When you pass an explicit token format, that format is the source of truth for the time wheels and the footer time pill: a 12-hour format (h/hh with A/a) drives a 12-hour wheel with an AM/PM column, and a 24-hour format (H/HH) drives a 24-hour wheel — regardless of hourFormat. So hourFormat only applies when you don't pass a format. (If format is an Intl.DateTimeFormat options object rather than a token string, the cycle can't be read from it and the wheel/pill fall back to hourFormat.)


CSS & Sass Variables​

DateTimeInput registers these variables on its own .input element. Override them there (or via className) — a value set on an ancestor is only inherited, and loses to the component-level declaration. See Theme.

CSS VariableSass VariableDefault
--bulma-input-h$input-hvar(--bulma-scheme-h)
--bulma-input-s$input-svar(--bulma-scheme-s)
--bulma-input-l$input-lvar(--bulma-scheme-main-l)
--bulma-input-border-style$input-border-stylesolid
--bulma-input-border-width$input-border-widthvar(--bulma-control-border-width)
--bulma-input-border-l$input-border-lvar(--bulma-border-l)
--bulma-input-border-l-delta$input-border-l-delta0%
--bulma-input-border-color$input-border-colorhsl(var(--bulma-input-h), var(--bulma-input-s), calc(var(--bulma-input-border-l) + var(--bulma-input-border-l-delta)))
--bulma-input-hover-border-l-delta$input-hover-border-l-deltavar(--bulma-hover-border-l-delta)
--bulma-input-active-border-l-delta$input-active-border-l-deltavar(--bulma-active-border-l-delta)
--bulma-input-focus-h$input-focus-hvar(--bulma-focus-h)
--bulma-input-focus-s$input-focus-svar(--bulma-focus-s)
--bulma-input-focus-l$input-focus-lvar(--bulma-focus-l)
--bulma-input-focus-shadow-size$input-focus-shadow-sizevar(--bulma-focus-shadow-size)
--bulma-input-focus-shadow-alpha$input-focus-shadow-alphavar(--bulma-focus-shadow-alpha)
--bulma-input-color-l$input-color-lvar(--bulma-text-strong-l)
--bulma-input-background-l$input-background-lvar(--bulma-scheme-main-l)
--bulma-input-background-l-delta$input-background-l-delta0%
--bulma-input-height$input-heightvar(--bulma-control-height)
--bulma-input-shadow$input-shadowinset 0 0.0625em 0.125em hsla(var(--bulma-scheme-h), var(--bulma-scheme-s), var(--bulma-scheme-invert-l), 0.05)
--bulma-input-placeholder-color$input-placeholder-colorhsla(var(--bulma-text-h), var(--bulma-text-s), var(--bulma-text-strong-l), 0.3)
--bulma-input-disabled-color$input-disabled-colorvar(--bulma-text-weak)
--bulma-input-disabled-background-color$input-disabled-background-colorvar(--bulma-background)
--bulma-input-disabled-border-color$input-disabled-border-colorvar(--bulma-background)
--bulma-input-disabled-placeholder-color$input-disabled-placeholder-colorhsla(var(--bulma-text-h), var(--bulma-text-s), var(--bulma-text-weak-l), 0.3)
--bulma-input-arrow$input-arrowvar(--bulma-link)
--bulma-input-icon-color$input-icon-colorvar(--bulma-text-light)
--bulma-input-icon-hover-color$input-icon-hover-colorvar(--bulma-text-weak)
--bulma-input-icon-focus-color$input-icon-focus-colorvar(--bulma-link)
--bulma-input-radius$input-radiusvar(--bulma-radius)