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

useMuiXTimePickerAdapter

Returns adapter props for building custom MUI X TimePicker integrations with React Hook Form, including minTime, maxTime, minutesStep and shouldDisableTime validation.

useMuiXTimePickerAdapter connects a field to React Hook Form and returns props you spread onto MUI X's TimePicker. It's what MuiXTimePickerElement uses internally.

Use the element for most forms. Reach for the hook when you want your own time field component, for example one that always enforces opening hours or a minute step, or one that shares form wiring across several pickers. This page describes the time validation that all time hooks share.

Import

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

Usage

This ShiftStartField only accepts times between 08:00 and 18:00 on the quarter hour, with its own messages. Submit it empty, or type 07:30 or 09:10, to see the validation.

hh:mm

08:00 to 18:00, every 15 minutes

"use client";import dayjs, { type Dayjs } from "dayjs";import Stack from "@mui/material/Stack";import { LocalizationProvider, TimePicker } from "@mui/x-date-pickers";import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";import { MuiButtonElement } from "@piplup/rhf-adapters/mui-material";import { useMuiXTimePickerAdapter } from "@piplup/rhf-adapters/mui-x-date-pickers";import { type Control, useForm } from "react-hook-form";type FormValues = { shiftStart: Dayjs | null };// Only the time of day is compared, so the date part of these values does not matter.const opensAt = dayjs().hour(8).minute(0);const closesAt = dayjs().hour(18).minute(0);// Shifts start between 08:00 and 18:00, on the quarter hour.function ShiftStartField({ control }: { control: Control<FormValues> }) {  const adapter = useMuiXTimePickerAdapter<Dayjs, FormValues, "shiftStart", HTMLDivElement>({    control,    name: "shiftStart",    required: true,    composeHelperText: true,    minTime: opensAt,    maxTime: closesAt,    minutesStep: 15,    messages: {      required: "Pick a start time",      minTime: "We open at 08:00",      maxTime: "The last shift starts at 18:00",      minutesStep: "Use a quarter hour (:00, :15, :30 or :45)",    },    slotProps: {      textField: { helperText: "08:00 to 18:00, every 15 minutes" },    },  });  return <TimePicker label="Shift start" ampm={false} {...adapter} />;}export default function Page() {  const { control, handleSubmit } = useForm<FormValues>({    defaultValues: { shiftStart: null },  });  return (    <LocalizationProvider dateAdapter={AdapterDayjs}>      <form        noValidate        onSubmit={handleSubmit((values) => alert(values.shiftStart?.format("HH:mm")))}      >        <Stack direction="column" spacing={2}>          <ShiftStartField control={control} />          <div>            <MuiButtonElement control={control} variant="contained" type="submit">              Save shift            </MuiButtonElement>          </div>        </Stack>      </form>    </LocalizationProvider>  );}

The hook must run inside a LocalizationProvider, because it reads the date adapter to validate values.

How it works

Value. The field value is passed to the picker as-is, with undefined converted to null so the picker stays controlled. Pass transform if your form stores something other than a date object.

Validation props reach the picker too. minTime, maxTime, minutesStep, shouldDisableTime, disablePast, disableFuture and disableIgnoringDatePartForTimeValidation are returned by the hook, so the picker greys out the same times that validation rejects. disablePast and disableFuture default to false.

Validation. The hook runs MUI X's validateTime on every non-empty value. Empty values pass; add required to reject them. Checks run in this order and only the first failure is reported:

OrderKeyFails whenDefault message
1invalidDatethe value is not a valid date, e.g. a half-typed timeDate is not a valid date
2minTimethe time is before minTimeTime is earlier than the minimum allowed time
3maxTimethe time is after maxTimeTime is later than the maximum allowed time
4disableFuturethe value is after nowFuture date is not allowed
5disablePastthe value is before nowPast date is not allowed
6shouldDisableTime-hoursshouldDisableTime(value, "hours") returns trueHour is disabled
7shouldDisableTime-minutesshouldDisableTime(value, "minutes") returns trueMinute is disabled
8shouldDisableTime-secondsshouldDisableTime(value, "seconds") returns trueSecond is disabled
9minutesStepthe minutes are not a multiple of minutesStepMinute step is not valid

required uses "Please fill out this field." The messages above are the exact default text.

Time of day only. minTime and maxTime compare only the time of day, so the date part of the values you pass is ignored. Set disableIgnoringDatePartForTimeValidation to compare full date-times instead. disablePast and disableFuture always compare against the current date and time.

Timezone. Validation uses the timezone prop when given, otherwise the timezone of the value.

Custom messages. Pass any key from the table in messages, as a string or a function. A function receives the value, and for minTime, maxTime and minutesStep also the limit, for example (value, minTime) => "Open from " + minTime.format("HH:mm"). invalidDate is typed as a function only, so write it as invalidDate: () => "Enter a valid time".

Errors and helper text. The returned slotProps.textField sets error and helperText from the field state and merges your own slotProps.textField (an object or a function). The hook has no top-level helperText prop; put your hint in slotProps.textField.helperText. With composeHelperText: true an error message replaces that hint while the field is invalid. Without it, only the error state is applied.

Blur and touched. The returned slotProps.textField calls React Hook Form's onBlur, so typing a time and tabbing away marks the field as touched. The returned onClose also calls onBlur and then your own onClose, so closing the picker counts as a blur too.

Your own rules.validate. A validate function or object in rules runs next to the built-in time validation, not instead of it.

Refs. React Hook Form's ref, merged with your inputRef, is returned as inputRef, so setFocus and focus-on-error move focus to the input. The hook's second argument becomes ref on the picker root.

Variants

The other time hooks use the same validation and messages. They differ in where errors can render and how a field becomes touched:

HookText fieldErrors shown byTouched on
useMuiXDesktopTimePickerAdapteryesslotProps.textFieldinput blur or picker close
useMuiXMobileTimePickerAdapteryesslotProps.textFieldinput blur or dialog close
useMuiXStaticTimePickerAdapternoyou render themnever
useMuiXTimeFieldAdapteryes, no popuperror / helperText propsinput blur
useMuiXTimeClockAdapternoyou render themblur inside the clock
useMuiXDigitalClockAdapternoyou render themblur inside the clock
useMuiXMultiSectionDigitalClockAdapternoyou render themblur inside the clock

Props

Prop

Type

Return value

Prop

Type

On this page