useMuiXDateCalendarAdapter
Returns adapter props for MUI X DateCalendar with React Hook Form. The calendar has no text field, so you remove error props before spreading and render the message yourself.
useMuiXDateCalendarAdapter returns React Hook Form props for MUI X's DateCalendar, the inline month grid. It's what MuiXDateCalendarElement uses internally.
The element validates but shows no message. Use the hook when the calendar is the input and users need to see why a date was rejected, or when you want to place the message, a label or a summary around the grid yourself.
Import
import { useMuiXDateCalendarAdapter } from "@piplup/rhf-adapters/mui-x-date-pickers";Usage
This DeliveryCalendar starts with a saved date from yesterday. The calendar shows it, but validation rejects it on submit. Sundays and dates more than 30 days ahead are blocked too.
"use client";import dayjs, { type Dayjs } from "dayjs";import FormHelperText from "@mui/material/FormHelperText";import Stack from "@mui/material/Stack";import { DateCalendar, LocalizationProvider } from "@mui/x-date-pickers";import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";import { MuiButtonElement } from "@piplup/rhf-adapters/mui-material";import { useMuiXDateCalendarAdapter } from "@piplup/rhf-adapters/mui-x-date-pickers";import { type Control, useForm } from "react-hook-form";type FormValues = { delivery: Dayjs | null };// DateCalendar has no text field, so this component renders the error itself.function DeliveryCalendar({ control }: { control: Control<FormValues> }) { const { error, helperText, required: _required, ...adapter } = useMuiXDateCalendarAdapter< Dayjs, FormValues, "delivery", HTMLDivElement >({ control, name: "delivery", required: true, composeHelperText: true, helperText: "We deliver Monday to Saturday.", disablePast: true, maxDate: dayjs().add(30, "day"), shouldDisableDate: (date) => date.day() === 0, messages: { required: "Choose a delivery day", disablePast: "That day has passed. Pick a new one.", shouldDisableDate: "We don't deliver on Sundays", maxDate: "We only schedule 30 days ahead", }, }); return ( <div> <DateCalendar {...adapter} /> <FormHelperText error={error}>{helperText}</FormHelperText> </div> );}export default function Page() { const { control, handleSubmit } = useForm<FormValues>({ // A saved draft from yesterday: the calendar shows it, validation rejects it. defaultValues: { delivery: dayjs().subtract(1, "day") }, }); return ( <LocalizationProvider dateAdapter={AdapterDayjs}> <form noValidate onSubmit={handleSubmit((values) => alert(values.delivery?.format("dddd, D MMMM YYYY")), )} > <Stack direction="column" spacing={2}> <DeliveryCalendar control={control} /> <div> <MuiButtonElement control={control} variant="contained" type="submit"> Schedule delivery </MuiButtonElement> </div> </Stack> </form> </LocalizationProvider> );}How it works
Validation uses the same rules, keys and default messages as useMuiXDatePickerAdapter. The validation props are also spread onto the calendar, so blocked days are greyed out and cannot be clicked. That means the error usually comes from a value the user did not click: defaultValues, reset, setValue, or a date that became invalid over time, like the one in the example.
Remove the error props before spreading. The hook returns error, helperText and required with the other props. DateCalendar has no props with those names and passes unknown props to its root <div>, so they would end up in the DOM. Take them off first, as the example does:
const { error, helperText, required: _required, ...adapter } = useMuiXDateCalendarAdapter({
control,
name: "delivery",
composeHelperText: true,
helperText: "We deliver Monday to Saturday.",
});
return (
<div>
<DateCalendar {...adapter} />
<FormHelperText error={error}>{helperText}</FormHelperText>
</div>
);With composeHelperText: true, helperText is your hint while the value is valid and the error message when it is not.
Blur. The returned onBlur is passed to the calendar's root element. React's onBlur on a container fires when focus leaves any element inside it, so the field is marked as touched once focus moves away from a day button, even to another day in the same grid.
Ref. React Hook Form's ref and your ref are merged and attached to the root <div>. It is not focusable, so setFocus and shouldFocusError do not move focus to the calendar.
Props
Prop
Type
Return value
Prop
Type