Piplup
@piplup/rhf-adaptersmui-x-date-pickersHooks

useMuiXDateTimePickerAdapter

Returns adapter props for MUI X DateTimePicker with React Hook Form, including time rules such as minTime, maxTime, minutesStep and shouldDisableTime.

useMuiXDateTimePickerAdapter connects a field to React Hook Form and returns props you spread onto MUI X's DateTimePicker. It's what MuiXDateTimePickerElement uses internally.

Use the element for most forms. Use the hook when one kind of date-time field repeats across your app with the same rules, such as booking slots limited to opening hours, and you want those rules and messages defined once in your own component.

Import

import { useMuiXDateTimePickerAdapter } from "@piplup/rhf-adapters/mui-x-date-pickers";

Usage

This MeetingField accepts times from 09:00 to 17:00 in 15 minute steps, on any day from tomorrow. Type a time such as 08:30 or 10:07 and submit to see the time rules.

MM/DD/YYYY hh:mm

From tomorrow, 09:00 to 17:00

"use client";import dayjs, { type Dayjs } from "dayjs";import Stack from "@mui/material/Stack";import { DateTimePicker, LocalizationProvider } from "@mui/x-date-pickers";import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";import { MuiButtonElement } from "@piplup/rhf-adapters/mui-material";import { useMuiXDateTimePickerAdapter } from "@piplup/rhf-adapters/mui-x-date-pickers";import { type Control, useForm } from "react-hook-form";type FormValues = { meeting: Dayjs | null };// Office hours: 09:00 to 17:00 on any day, in 15 minute slots, booked from tomorrow.// minTime and maxTime compare the time of day only, so they apply to every date.function MeetingField({ control }: { control: Control<FormValues> }) {  const adapter = useMuiXDateTimePickerAdapter<Dayjs, FormValues, "meeting", HTMLDivElement>({    control,    name: "meeting",    required: true,    composeHelperText: true,    // The date-time hooks type minDate and maxDate as required.    minDate: dayjs().add(1, "day").startOf("day"),    maxDate: dayjs().add(3, "month"),    minTime: dayjs().hour(9).startOf("hour"),    maxTime: dayjs().hour(17).startOf("hour"),    minutesStep: 15,    messages: {      required: "Pick a meeting time",      minDate: "Meetings can be booked from tomorrow",      maxDate: "Meetings can be booked up to three months ahead",      minTime: "Meetings start at 09:00 or later",      maxTime: "Meetings must start by 17:00",      minutesStep: "Use a 15 minute slot (:00, :15, :30 or :45)",    },    slotProps: {      textField: { helperText: "From tomorrow, 09:00 to 17:00" },    },  });  return <DateTimePicker label="Meeting" ampm={false} {...adapter} />;}export default function Page() {  const { control, handleSubmit } = useForm<FormValues>({    defaultValues: { meeting: null },  });  return (    <LocalizationProvider dateAdapter={AdapterDayjs}>      <form        noValidate        onSubmit={handleSubmit((values) =>          alert(values.meeting?.format("ddd D MMM, HH:mm")),        )}      >        <Stack direction="column" spacing={2}>          <MeetingField control={control} />          <div>            <MuiButtonElement control={control} variant="contained" type="submit">              Book meeting            </MuiButtonElement>          </div>        </Stack>      </form>    </LocalizationProvider>  );}

Like the date-only hook, it must run inside a LocalizationProvider.

How it works

Value handling, the text field wiring (slotProps.textField, composeHelperText) and the "closing the picker counts as a blur" behavior are the same as in useMuiXDatePickerAdapter. What differs is validation.

Date checks, then time checks. The hook runs MUI X's validateDateTime. It first runs every date check from the DatePicker hook (minDate, maxDate, shouldDisableDate and so on, with the same keys and default messages). If those pass, it checks the time, in this order:

KeyFails whenDefault message
minTimeThe time is before minTimeTime is earlier than the minimum allowed time
maxTimeThe time is after maxTimeTime is later than the maximum allowed time
disableFutureThe value is after the current momentFuture date is not allowed
disablePastThe value is before the current momentPast date is not allowed
shouldDisableTime-hoursshouldDisableTime(value, "hours") returns trueHour is disabled
shouldDisableTime-minutesshouldDisableTime(value, "minutes") returns trueMinute is disabled
shouldDisableTime-secondsshouldDisableTime(value, "seconds") returns trueSecond is disabled
minutesStepThe minutes are not a multiple of minutesStepMinute step is not valid

Only the first failing check is reported. Override any message through messages, using the key from the table.

disablePast and disableFuture work to the minute. The date check compares by day, and the time check compares against the current time. So with disablePast, 08:00 today fails if it is already 10:00.

minTime and maxTime ignore the date by default. Only the time of day is compared, so minTime set to 09:00 means "09:00 or later on any day". Set disableIgnoringDatePartForTimeValidation: true to compare the full date and time instead. To express "not before this exact moment", pass the same value to minDate and minTime with that flag. The MobileDateTimePicker hook page has an example.

The hook and the picker decide this flag differently. The hook uses the value you pass. MUI X's DateTimePicker ignores that prop and turns full date comparison on whenever disablePast, disableFuture, minDateTime or maxDateTime is set. If you combine a time-of-day minTime with disablePast, the picker lets users choose 07:00 tomorrow and the form then rejects it. Keep them aligned: use time-of-day limits without disablePast/disableFuture, or set the flag together with disablePast for an exact-moment limit.

The hook does not read minDateTime or maxDateTime. If you pass them straight to the picker, the picker greys out those times but the form still accepts a typed value outside the range. Use minTime/maxTime with the flag above so the rule is validated too.

The rules also drive the picker UI. The validation props are returned with the adapter props, so spreading them onto DateTimePicker also disables the matching days, hours and minutes in its views (subject to the flag difference above). The form-level check still matters for typed values and for values set with reset or setValue.

In TypeScript, minDate and maxDate are required props of the date-time hooks, unlike the date-only hooks. Pass real limits, as the example does. If they are missing at runtime, the hook falls back to the LocalizationProvider defaults (1900-01-01 and 2099-12-31).

Props

Prop

Type

Return value

Prop

Type

On this page