Skip to content
+

Migration from v7 to v8

This guide describes the changes needed to migrate the Date and Time Pickers from v7 to v8.

Introduction

This is a reference guide for upgrading @mui/x-date-pickers from v7 to v8.

Start using the new release

In package.json, change the version of the date pickers package to next.

-"@mui/x-date-pickers": "7.x.x",
+"@mui/x-date-pickers": "next",

Using next ensures that it will always use the latest v8 pre-release version, but you can also use a fixed version, like 8.0.0-alpha.0.

Since v8 is a major release, it contains changes that affect the public API. These changes were done for consistency, improved stability and to make room for new features. Described below are the steps needed to migrate from v7 to v8.

Run codemods

The preset-safe codemod will automatically adjust the bulk of your code to account for breaking changes in v8. You can run v8.0.0/pickers/preset-safe targeting only Date and Time Pickers or v8.0.0/preset-safe to target the other packages as well.

You can either run it on a specific file, folder, or your entire codebase when choosing the <path> argument.

// Date and Time Pickers specific
npx @mui/x-codemod@latest v8.0.0/pickers/preset-safe <path>

// Target the other packages as well
npx @mui/x-codemod@latest v8.0.0/preset-safe <path>

Breaking changes that are handled by this codemod are denoted by a ✅ emoji in the table of contents on the right side of the screen.

If you have already applied the v8.0.0/pickers/preset-safe (or v8.0.0/preset-safe) codemod, then you should not need to take any further action on these items.

All other changes must be handled manually.

New DOM structure for the field

Before version v8.x, the fields' DOM structure consisted of an <input />, which held the whole value for the component, but unfortunately presents a few limitations in terms of accessibility when managing multiple section values.

Starting with version v8.x, all the field and picker components come with a new DOM structure that allows the field component to set aria attributes on individual sections, providing a far better experience with screen readers.

Fallback to the non-accessible DOM structure

<DateField enableAccessibleFieldDOMStructure={false} />
<DatePicker enableAccessibleFieldDOMStructure={false} />
<DateRangePicker enableAccessibleFieldDOMStructure={false} />

Migrate slotProps.field

When using slotProps.field to pass props to your field component, the field consumes some props (e.g: shouldRespectLeadingZeros) and forwards the rest to the TextField.

  • For the props consumed by the field, the behavior should remain exactly the same with both DOM structures.

    Both components below will respect the leading zeroes on digit sections:

    <DatePicker
      slotProps={{ field: { shouldRespectLeadingZeros: true } }}
      enableAccessibleFieldDOMStructure={false}
     />
    <DatePicker
      slotProps={{ field: { shouldRespectLeadingZeros: true } }}
    />
    
  • For the props forwarded to the TextField, you can have a look at the next section to see how the migration impact them.

    Both components below will render a small size UI:

    <DatePicker
      slotProps={{ field: { size: 'small' } }}
      enableAccessibleFieldDOMStructure={false}
     />
    <DatePicker
      slotProps={{ field: { size: 'small' } }}
    />
    

Migrate slotProps.textField

If you are passing props to slotProps.textField, these props will now be received by PickersTextField and should keep working the same way as before.

Both components below will render a small size UI:

<DatePicker
  slotProps={{ textField: { size: 'small' } }}
  enableAccessibleFieldDOMStructure={false}
/>
<DatePicker
  slotProps={{ textField: { size: 'small' } }}
/>

Migrate slots.field

If you are passing a custom field component to your pickers, you need to create a new one that is using the accessible DOM structure. This new component will need to use the PickersSectionList component instead of an <input /> HTML element.

You can have a look at the Using a custom input to have a concrete example.

Migrate slots.textField

If you are passing a custom TextField component to your fields and pickers, you need to create a new one that is using the accessible DOM structure.

You can have a look at the second demo of the Wrapping PickersTextField to have a concrete example.

Migrate the theme

If you are using the theme to customize MuiTextField, you need to pass the same config to MuiPickersTextField:

const theme = createTheme({
  components: {
    MuiTextField: {
      defaultProps: {
        variant: 'outlined',
      },
      styleOverrides: {
        root: {
          '& .MuiInputLabel-outlined.Mui-focused': {
            color: 'red',
          },
        },
      },
    },
    MuiPickersTextField: {
      defaultProps: {
        variant: 'outlined',
      },
      styleOverrides: {
        root: {
          '& .MuiInputLabel-outlined.Mui-focused': {
            color: 'red',
          },
        },
      },
    },
  },
});

If you are using the theme to customize MuiInput, MuiOutlinedInput or MuiFilledInput, you need to pass the same config to MuiPickersInput, MuiPickersOutlinedInput or MuiPickersFilledInput:

const theme = createTheme({
  components: {
    // Replace with `MuiOutlinedInput` or `MuiFilledInput` if needed
    MuiInput: {
      defaultProps: {
        margin: 'dense',
      },
      styleOverrides: {
        root: {
          color: 'red',
        },
      },
    },
    // Replace with `MuiPickersOutlinedInput` or `MuiPickersFilledInput` if needed
    MuiPickersInput: {
      defaultProps: {
        margin: 'dense',
      },
      styleOverrides: {
        root: {
          color: 'red',
        },
      },
    },
  },
});

If you are using the theme to customize MuiInputBase, you need to pass the same config to MuiPickersInputBase:

const theme = createTheme({
  components: {
    MuiInputBase: {
      defaultProps: {
        margin: 'dense',
      },
      styleOverrides: {
        root: {
          color: 'red',
        },
      },
    },
    MuiPickersInputBase: {
      defaultProps: {
        margin: 'dense',
      },
      styleOverrides: {
        root: {
          color: 'red',
        },
      },
    },
  },
});

Removed types

The following types are no longer exported by @mui/x-date-pickers and/or @mui/x-date-pickers-pro. If you were using them, you need to replace them with the following code:

  • UseDateFieldComponentProps

    import { UseDateFieldProps } from '@mui/x-date-pickers/DateField';
    import { PickerValidDate } from '@mui/x-date-pickers/models';
    
    type UseDateFieldComponentProps<
      TDate extends PickerValidDate,
      TEnableAccessibleFieldDOMStructure extends boolean,
      TChildProps extends {},
    > = Omit<
      TChildProps,
      keyof UseDateFieldProps<TDate, TEnableAccessibleFieldDOMStructure>
    > &
      UseDateFieldProps<TDate, TEnableAccessibleFieldDOMStructure>;
    
  • UseTimeFieldComponentProps

    import { UseTimeFieldProps } from '@mui/x-date-pickers/TimeField';
    import { PickerValidDate } from '@mui/x-date-pickers/models';
    
    type UseTimeFieldComponentProps<
      TDate extends PickerValidDate,
      TEnableAccessibleFieldDOMStructure extends boolean,
      TChildProps extends {},
    > = Omit<
      TChildProps,
      keyof UseTimeFieldProps<TDate, TEnableAccessibleFieldDOMStructure>
    > &
      UseTimeFieldProps<TDate, TEnableAccessibleFieldDOMStructure>;
    
  • UseDateTimeFieldComponentProps

    import { UseDateTimeFieldProps } from '@mui/x-date-pickers/DateTimeField';
    import { PickerValidDate } from '@mui/x-date-pickers/models';
    
    type UseDateTimeFieldComponentProps<
      TDate extends PickerValidDate,
      TEnableAccessibleFieldDOMStructure extends boolean,
      TChildProps extends {},
    > = Omit<
      TChildProps,
      keyof UseDateTimeFieldProps<TDate, TEnableAccessibleFieldDOMStructure>
    > &
      UseDateTimeFieldProps<TDate, TEnableAccessibleFieldDOMStructure>;
    

Stop passing utils and the date object to some translation keys

Some translation keys no longer require utils and the date object as parameters, but only the formatted value as a string. The keys affected by this changes are: clockLabelText, openDatePickerDialogue and openTimePickerDialogue. If you have customized those translation keys, you have to update them following the examples below:

  • If you are setting a custom value in a picker component:
-clockLabelText: (view, time, utils) =>
-   `Select ${view}. ${
-     time === null || !utils.isValid(time)
-       ? 'No time selected'
-       : `Selected time is ${utils.format(time, 'fullTime')}`
-   }`
+clockLabelText: (view, formattedTime) =>
+   `Select ${view}. ${
+     formattedTime == null ? 'No time selected' : `Selected time is ${formattedTime}`
+   }`

-openDatePickerDialogue: (value, utils) =>
-  value !== null && utils.isValid(value)
-    ? `Choose date, selected date is ${utils.format(value, 'fullDate')}`
-    : 'Choose date',
+openDatePickerDialogue: (formattedDate) =>
+  formattedDate ? `Choose date, selected date is ${formattedDate}` : 'Choose date'

-openTimePickerDialogue: (value, utils) =>
-  value !== null && utils.isValid(value)
-    ? `Choose time, selected time is ${utils.format(value, 'fullTime')}`
-    : 'Choose time',
+openTimePickerDialogue: (formattedTime) =>
+  formattedTime ? `Choose time, selected time is ${formattedTime}` : 'Choose time'
  • If you are setting a custom value in the LocalizationProvider:
 <LocalizationProvider localeText={{
-   clockLabelText: (view, time, utils) =>
-     `Select ${view}. ${
-       time === null || !utils.isValid(time)
-         ? 'No time selected'
-         : `Selected time is ${utils.format(time, 'fullTime')}`
-     }`
+   clockLabelText: (view, formattedTime) =>
+     `Select ${view}. ${
+       formattedTime == null ? 'No time selected' : `Selected time is ${formattedTime}`
+     }`
-   openDatePickerDialogue: (value, utils) =>
-     value !== null && utils.isValid(value)
-      ? `Choose date, selected date is ${utils.format(value, 'fullDate')}`
-      : 'Choose date',
+   openDatePickerDialogue: (formattedDate) =>
+     formattedDate ? `Choose date, selected date is ${formattedDate}` : 'Choose date'
-   openTimePickerDialogue: (value, utils) =>
-     value !== null && utils.isValid(value)
-       ? `Choose time, selected time is ${utils.format(value, 'fullTime')}`
-       : 'Choose time',
+   openTimePickerDialogue: (formattedTime) =>
+     formattedTime ? `Choose time, selected time is ${formattedTime}` : 'Choose time'
 }} >
  • If you using this translation key in a custom component:
 const translations = usePickersTranslations();

-const clockLabelText = translations.clockLabelText(
-  view,
-  value,
-  {} as any,
-  value == null ? null : value.format('hh:mm:ss')
-);
+const clockLabelText = translations.clockLabelText(
+  view,
+  value == null ? null : value.format('hh:mm:ss')
+);

-const openDatePickerDialogue = translations.openDatePickerDialogue(
-  value,
-  {} as any,
-  value == null ? null : value.format('MM/DD/YYY')
-);
+const openDatePickerDialogue = translations.openDatePickerDialogue(
+  value == null ? null : value.format('MM/DD/YYY')
+);

-const openTimePickerDialogue = translations.openTimePickerDialogue(
-  value,
-  {} as any,
-  value == null ? null : value.format('hh:mm:ss')
-);
+const openTimePickerDialogue = translations.openTimePickerDialogue(
+  value == null ? null : value.format('hh:mm:ss')
+);

Also the following types and interfaces no longer receive a generic type parameter:

  • PickersComponentAgnosticLocaleText
  • PickersInputComponentLocaleText
  • PickersInputLocaleText
  • PickersLocaleText
  • PickersTranslationKeys