Migration from v4 to v5
Yeah, v5 has been released!
If you're looking for the v4 docs, you can find them here.
Introduction
This is a reference for upgrading your site from Material-UI v4 to v5. While there's a lot covered here, you probably won't need to do everything. We'll do our best to keep things easy to follow, and as sequential as possible, so you can quickly get rocking on v5!
Why you should migrate
This documentation page covers the how of migrating from v4 to v5. The why will be covered in an upcoming blog post on Medium.
Migration steps
- Update React & TypeScript
- ThemeProvider setup
- Update Material-UI
- Run Codemod
- Handling Breaking Changes
- Migrate theme's
styleOverridesto emotion - Migrate
makeStylesto emotion - Troubleshooting
💡 Prefer to create small commits on any changes to help the migration goes smoother. If you encounter any issue, check Troubleshooting section. For other unknown error, create an issue with this title format
[Migration] Summary of your issue.
Update React & TypeScript version
The minimum supported version of React was increased from v16.8.0 to v17.0.0.
The minimum supported version of TypeScript was increased from v3.2 to v3.5.
We try to align with types released from DefinitelyTyped (i.e. packages published on npm under the
@typesnamespace). We will not change the minimum supported version in a major version of Material-UI. However, we generally recommend to not use a TypeScript version older than the lowest supported version of DefinitelyTyped
Note: if your project includes these packages, please upgrade them to the latest version.
react-scripts@types/react@types/react-dom
📝 Please make sure that your application is still running without error and commit the change before continuing the next step.
ThemeProvider setup
Make sure that ThemeProvider is defined at the root of your application (even if you are using the default theme) and NO useStyles is called before <ThemeProvider>. This is because we are going to use @material-ui/styles temporarily which require ThemeProvider.
import { ThemeProvider, createMuiTheme, makeStyles } from '@material-ui/core/styles';
const theme = createMuiTheme();
const useStyles = makeStyles((theme) => {
root: {
// some css that access to theme
}
});
function App() {
const classes = useStyles(); // ❌ If you have this, consider moving <ThemeProvider> to HOC and wrap the App
return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
}
📝 Please make sure that your application is still running without error and commit the change before continuing the next step.
Update Material-UI version
To use the next version of Material-UI.
💡 If you want to use Material-UI v5 with styled-components instead of emotion, check out the installation guide
npm install @material-ui/core@next @emotion/react @emotion/styled
// or with `yarn`
yarn add @material-ui/core@next @emotion/react @emotion/styled
Optional if your project includes @material-ui/icons and/or @material-ui/lab, use the next version of them.
npm install @material-ui/icons@next @material-ui/lab@next
// or with `yarn`
yarn add @material-ui/icons@next @material-ui/lab@next
Note: if you are using
@material-ui/pickers, it has moved to@material-ui/lab. The details is in "Handling breaking changes" section.
In this migration process, you will need to install/update @material-ui/styles (JSS) for temporary transition to v5.
npm install @material-ui/styles@next
// or with `yarn`
yarn add @material-ui/styles@next
⚠️ After this step, your application is expected to crash because the style library has changed from JSS (v4) to emotion (v5).
Run codemods
We have prepared these codemods to ease your migration experience.
preset-safe
This codemod contains most of the transformers that are useful for migration. (This codemod should be applied only once per folder)
npx @material-ui/codemod@next v5.0.0/preset-safe <path>
If you want to run the transformers one by one, check out preset-safe codemod for more details.
variant-prop
Transform <TextField/>, <FormControl/>, <Select/> component by applying variant="standard" if no variant is defined (because default variant has changed from standard in v4 to outlined in v5).
❗️ You should NOT use this codemod if you have already defined default
variant: "outlined"in the theme.
// if you have theme setup like this, ❌ don't run this codemod.
// these default props can be removed later because `outlined` is the default value in v5
createMuiTheme({
props: {
MuiTextField: {
variant: 'outlined',
},
},
});
However, if you want to keep variant="standard" to you components, run this codemod or configure theme default props.
npx @material-ui/codemod@next v5.0.0/variant-prop <path>
For more details, checkout variant-prop codemod.
link-underline-hover
Transform <Link/> component by apply underline="hover" if no underline prop defined (because default underline has changed from "hover" in v4 to "always" in v5).
❗️ You should NOT use this codemod if you already define default
underline: "always"in the theme.
// if you have theme setup like this, ❌ don't run this codemod.
// this default props can be removed later because `always` is the default value in v5
createMuiTheme({
props: {
MuiLink: {
underline: 'always',
},
},
});
However, if you want to keep variant="hover" to you components, run this codemod or configure theme default props.
npx @material-ui/codemod@next v5.0.0/link-underline-hover <path>
For more details, checkout link-underline-hover codemod.
Once you have completed the codemod step, try running your application again. At this point, it should be running without error. Otherwise check out the Troubleshooting section. Next step, handling breaking changes in each component.
Handling breaking changes
Supported browsers and node versions
The targets of the default bundle have changed.
The exact versions will be pinned on release from the browserslist query "> 0.5%, last 2 versions, Firefox ESR, not dead, not IE 11, maintained node versions".
The default bundle supports the following minimum versions:
- Node 12 (up from 8)
- Chrome 84 (up from 49)
- Edge 85 (up from 14)
- Firefox 78 (up from 52)
- Safari 13 (macOS) and 12.2 (iOS) (up from 10)
- and more (see .browserslistrc (
stableentry))
It no longer supports IE 11. If you need to support IE 11, check out our legacy bundle.
non-ref-forwarding class components
Support for non-ref-forwarding class components in the component prop or as immediate children has been dropped. If you were using unstable_createStrictModeTheme or didn't see any warnings related to findDOMNode in React.StrictMode then you don't need to do anything.
Otherwise check out the Caveat with refs section in the composition guide to find out how to migrate.
This change affects almost all components where you're using the component prop or passing children to components that require children to be elements (e.g. <MenuList><CustomMenuItem /></MenuList>)
Style library
The style library used by default in v5 is emotion. While migrating from JSS to emotion, and if you are using JSS style overrides for your components (for example overrides created by makeStyles), you will need to take care of the CSS injection order. To do so, you need to have the StyledEngineProvider with the injectFirst option at the top of your component tree.
✅ This is handled in 🪄preset-safe codemod.
Here is an example:
import * as React from 'react';
import { StyledEngineProvider } from '@material-ui/core/styles';
export default function GlobalCssPriority() {
return (
{/* Inject emotion before JSS */}
<StyledEngineProvider injectFirst>
{/* Your component tree. Now you can override Material-UI's styles. */}
</StyledEngineProvider>
);
}
Note: If you are using emotion to style your app, and have a custom cache, it will override the one provided by Material-UI. In order for the injection order to still be correct, you need to add the
prependoption tocreateCache.✅ This is handled in 🪄preset-safe codemod.
Here is an example:
import * as React from 'react';
import { CacheProvider } from '@emotion/react';
import createCache from '@emotion/cache';
const cache = createCache({
key: 'css',
+ prepend: true,
});
export default function PlainCssPriority() {
return (
<CacheProvider value={cache}>
{/* Your component tree. Now you can override Material-UI's styles. */}
</CacheProvider>
);
}
Note: If you are using styled-components and have
StyleSheetManagerwith a customtarget, make sure that the target is the first element in the HTML<head>. To see how it can be done, take a look at theStyledEngineProviderimplementation in the@material-ui/styled-engine-scpackage.
Upgrade helper
For a smoother transition, the adaptV4Theme helper allows you to iteratively upgrade some of the theme changes to the new theme structure.
✅ This is handled in 🪄preset-safe codemod.
-import { createMuiTheme } from '@material-ui/core/styles';
+import { createTheme, adaptV4Theme } from '@material-ui/core/styles';
-const theme = createMuiTheme({
+const theme = createTheme(adaptV4Theme({
// v4 theme
-});
+}));
Supported changes
The following changes are supported by the adapter:
The "gutters" abstraction hasn't proven to be used frequently enough to be valuable.
-theme.mixins.gutters(), +paddingLeft: theme.spacing(2), +paddingRight: theme.spacing(2), +[theme.breakpoints.up('sm')]: { + paddingLeft: theme.spacing(3), + paddingRight: theme.spacing(3), +},theme.spacingnow returns single values with px units by default. This change improves the integration with styled-components & emotion.✅ This is handled in 🪄preset-safe codemod by removing any 'px' suffix from
theme.spacingcalls in a template string.Before:
theme.spacing(2) => 16After:
theme.spacing(2) => '16px'The
theme.palette.typekey was renamed totheme.palette.mode, to better follow the "dark mode" term that is usually used for describing this feature.✅ This is handled in 🪄preset-safe codemod.
import { createTheme } from '@material-ui/core/styles'; -const theme = createTheme({palette: { type: 'dark' }}), +const theme = createTheme({palette: { mode: 'dark' }}),The default
theme.palette.infocolors was changed to pass AA standard contrast ratio in both light & dark mode.info = { - main: cyan[500], + main: lightBlue[700], // lightBlue[400] in "dark" mode - light: cyan[300], + light: lightBlue[500], // lightBlue[300] in "dark" mode - dark: cyan[700], + dark: lightBlue[900], // lightBlue[700] in "dark" mode }The default
theme.palette.successcolors was changed to pass AA standard contrast ratio in both light & dark mode.success = { - main: green[500], + main: green[800], // green[400] in "dark" mode - light: green[300], + light: green[500], // green[300] in "dark" mode - dark: green[700], + dark: green[900], // green[700] in "dark" mode }The default
theme.palette.warningcolors was changed to pass AA standard contrast ratio in both light & dark mode.warning = { - main: orange[500], + main: "#ED6C02", // orange[400] in "dark" mode - light: orange[300], + light: orange[500], // orange[300] in "dark" mode - dark: orange[700], + dark: orange[900], // orange[700] in "dark" mode }The
theme.palette.text.hintkey was unused in Material-UI components, and has been removed. If you depend on it, you can add it back:import { createTheme } from '@material-ui/core/styles'; -const theme = createTheme(), +const theme = createTheme({ + palette: { text: { hint: 'rgba(0, 0, 0, 0.38)' } }, +});The components' definitions in the theme were restructure under the
componentskey, to allow for easier discoverability of the definitions related to any one component.
props
import { createTheme } from '@material-ui/core/styles';
const theme = createTheme({
- props: {
- MuiButton: {
- disableRipple: true,
- },
- },
+ components: {
+ MuiButton: {
+ defaultProps: {
+ disableRipple: true,
+ },
+ },
+ },
});
overrides
import { createTheme } from '@material-ui/core/styles';
const theme = createTheme({
- overrides: {
- MuiButton: {
- root: { padding: 0 },
- },
- },
+ components: {
+ MuiButton: {
+ styleOverrides: {
+ root: { padding: 0 },
+ },
+ },
+ },
});
Styles
Renamed
fadetoalphato better describe its functionality. The previous name was leading to confusion when the input color already had an alpha value. The helper overrides the alpha value of the color.✅ This is handled in 🪄preset-safe codemod.
- import { fade } from '@material-ui/core/styles'; + import { alpha } from '@material-ui/core/styles'; const classes = makeStyles(theme => ({ - backgroundColor: fade(theme.palette.primary.main, theme.palette.action.selectedOpacity), + backgroundColor: alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity), }));The
createStylesfunction from@material-ui/core/styleswas moved to the one exported from@material-ui/styles. It is necessary for removing the dependency to@material-ui/stylesin the core package.✅ This is handled in 🪄preset-safe codemod.
-import { createStyles } from '@material-ui/core/styles'; +import { createStyles } from '@material-ui/styles';
@material-ui/styles
ThemeProvider
If you are using the utilities from @material-ui/styles together with the @material-ui/core, you should replace the use of ThemeProvider from @material-ui/styles with the one exported from @material-ui/core/styles. This way, the theme provided in the context will be available in both the styling utilities exported from @material-ui/styles, like makeStyles, withStyles etc. and the Material-UI components.
-import { ThemeProvider } from '@material-ui/styles';
+import { ThemeProvider } from '@material-ui/core/styles';
Make sure to add a ThemeProvider at the root of your application, as the defaultTheme is no longer available.
Default theme (TypeScript)
The @material-ui/styles package is no longer part of @material-ui/core/styles. If you are using @material-ui/styles together with @material-ui/core you need to add a module augmentation for the DefaultTheme.
✅ This is handled in 🪄preset-safe codemod.
// in your theme file that you call `createTheme()`
import { Theme } from '@material-ui/core/styles';
declare module '@material-ui/styles' {
interface DefaultTheme extends Theme {}
}
@material-ui/core/colors
Nested imports of more than 1 level are private. You can't import color from
@material-ui/core/colors/red.✅ This is handled in 🪄preset-safe codemod.
-import red from '@material-ui/core/colors/red'; +import { red } from '@material-ui/core/colors';
@material-ui/core/styles
createGenerateClassName
The
createGenerateClassNamefunction is no longer exported from@material-ui/core/styles. You should import it directly from@material-ui/styles.✅ This is handled in 🪄preset-safe codemod.
-import { createGenerateClassName } from '@material-ui/core/styles'; +import { createGenerateClassName } from '@material-ui/styles';
createMuiTheme
The function
createMuiThemewas renamed tocreateThemeto make it more intuitive to use withThemeProvider.✅ This is handled in 🪄preset-safe codemod.
-import { createMuiTheme } from '@material-ui/core/styles'; +import { createTheme } from '@material-ui/core/styles'; -const theme = createMuiTheme({ +const theme = createTheme({
jssPreset
The
jssPresetobject is no longer exported from@material-ui/core/styles. You should import it directly from@material-ui/styles.✅ This is handled in 🪄preset-safe codemod.
-import { jssPreset } from '@material-ui/core/styles'; +import { jssPreset } from '@material-ui/styles';
makeStyles
The
makeStylesJSS utility is no longer exported from@material-ui/core/styles. You can use@material-ui/styles/makeStylesinstead. Make sure to add aThemeProviderat the root of your application, as thedefaultThemeis no longer available. If you are using this utility together with@material-ui/core, it's recommended that you use theThemeProvidercomponent from@material-ui/core/stylesinstead.✅ This is handled in 🪄preset-safe codemod.
-import { makeStyles } from '@material-ui/core/styles'; +import { makeStyles } from '@material-ui/styles'; +import { createTheme, ThemeProvider } from '@material-ui/core/styles'; +const theme = createTheme(); const useStyles = makeStyles((theme) => ({ background: theme.palette.primary.main, })); function Component() { const classes = useStyles(); return <div className={classes.root} /> } // In the root of your app function App(props) { - return <Component />; + return <ThemeProvider theme={theme}><Component {...props} /></ThemeProvider>; }
MuiThemeProvider
The
MuiThemeProvidercomponent is no longer exported from@material-ui/core/styles. UseThemeProviderinstead.✅ This is handled in 🪄preset-safe codemod.
-import { MuiThemeProvider } from '@material-ui/core/styles'; +import { ThemeProvider } from '@material-ui/core/styles';
ServerStyleSheets
The
ServerStyleSheetscomponent is no longer exported from@material-ui/core/styles. You should import it directly from@material-ui/styles.✅ This is handled in 🪄preset-safe codemod.
-import { ServerStyleSheets } from '@material-ui/core/styles'; +import { ServerStyleSheets } from '@material-ui/styles';
styled
The
styledJSS utility is no longer exported from@material-ui/core/styles. You can use@material-ui/styles/styledinstead. Make sure to add aThemeProviderat the root of your application, as thedefaultThemeis no longer available. If you are using this utility together with@material-ui/core, it's recommended you use theThemeProvidercomponent from@material-ui/core/stylesinstead.-import { styled } from '@material-ui/core/styles'; +import { styled } from '@material-ui/styles'; +import { createTheme, ThemeProvider } from '@material-ui/core/styles'; +const theme = createTheme(); const MyComponent = styled('div')(({ theme }) => ({ background: theme.palette.primary.main })); function App(props) { - return <MyComponent />; + return <ThemeProvider theme={theme}><MyComponent {...props} /></ThemeProvider>; }
StylesProvider
The
StylesProvidercomponent is no longer exported from@material-ui/core/styles. You should import it directly from@material-ui/styles.✅ This is handled in 🪄preset-safe codemod.
-import { StylesProvider } from '@material-ui/core/styles'; +import { StylesProvider } from '@material-ui/styles';
useThemeVariants
The
useThemeVariantshook is no longer exported from@material-ui/core/styles. You should import it directly from@material-ui/styles.✅ This is handled in 🪄preset-safe codemod.
-import { useThemeVariants } from '@material-ui/core/styles'; +import { useThemeVariants } from '@material-ui/styles';
withStyles
Replace the
innerRefprop with therefprop. Refs are now automatically forwarded to the inner component.✅ This is handled in 🪄preset-safe codemod.
import * as React from 'react'; import { withStyles } from '@material-ui/styles'; const MyComponent = withStyles({ root: { backgroundColor: 'red', }, })(({ classes }) => <div className={classes.root} />); function MyOtherComponent(props) { const ref = React.useRef(); - return <MyComponent innerRef={ref} />; + return <MyComponent ref={ref} />; }The
withStylesJSS utility is no longer exported from@material-ui/core/styles. You can use@material-ui/styles/withStylesinstead. Make sure to add aThemeProviderat the root of your application, as thedefaultThemeis no longer available. If you are using this utility together with@material-ui/core, you should use theThemeProvidercomponent from@material-ui/core/stylesinstead.✅ This is handled in 🪄preset-safe codemod.
-import { withStyles } from '@material-ui/core/styles'; +import { withStyles } from '@material-ui/styles'; +import { createTheme, ThemeProvider } from '@material-ui/core/styles'; +const defaultTheme = createTheme(); const MyComponent = withStyles((props) => { const { classes, className, ...other } = props; return <div className={clsx(className, classes.root)} {...other} /> })(({ theme }) => ({ root: { background: theme.palette.primary.main }})); function App() { - return <MyComponent />; + return <ThemeProvider theme={defaultTheme}><MyComponent /></ThemeProvider>; }
withTheme
The
withThemeHOC utility has been removed from the@material-ui/core/stylespackage. You can use@material-ui/styles/withThemeinstead. Make sure to add aThemeProviderat the root of your application, as thedefaultThemeis no longer available. If you are using this utility together with@material-ui/core, it's recommended you use theThemeProvidercomponent from@material-ui/core/stylesinstead.✅ This is handled in 🪄preset-safe codemod.
-import { withTheme } from '@material-ui/core/styles'; +import { withTheme } from '@material-ui/styles'; +import { createTheme, ThemeProvider } from '@material-ui/core/styles'; +const theme = createTheme(); const MyComponent = withTheme(({ theme }) => <div>{props.theme.direction}</div>); function App(props) { - return <MyComponent />; + return <ThemeProvider theme={theme}><MyComponent {...props} /></ThemeProvider>; }Replace the
innerRefprop with therefprop. Refs are now automatically forwarded to the inner component.import * as React from 'react'; import { withTheme } from '@material-ui/styles'; const MyComponent = withTheme(({ theme }) => <div>{props.theme.direction}</div>); function MyOtherComponent(props) { const ref = React.useRef(); - return <MyComponent innerRef={ref} />; + return <MyComponent ref={ref} />; }
withWidth
This HOC was removed. There's an alternative using the
useMediaQueryhook.✅ This is handled in 🪄preset-safe codemod by applying hard-coded function to prevent the application from crashing.
@material-ui/pickers
We have a dedicated page for migrating @material-ui/pickers to v5
System
The following system functions (and properties) were renamed because they are considered deprecated CSS:
gridGaptogapgridRowGaptorowGapgridColumnGaptocolumnGap
✅ This is handled in 🪄preset-safe codemod.
Use spacing unit in
gap,rowGap, andcolumnGap. If you were using a number previously, you need to mention the px to bypass the new transformation withtheme.spacing.✅ This is handled in 🪄preset-safe codemod.
<Box - gap={2} + gap="2px" >Replace
cssprop withsxto avoid collision with styled-components & emotioncssprop.✅ This is handled in 🪄preset-safe codemod.
-<Box css={{ color: 'primary.main' }} /> +<Box sx={{ color: 'primary.main' }} />Note that the system grid function wasn't documented in v4.
Core components
As the core components use emotion as their style engine, the props used by emotion are not intercepted. The prop as in the following code snippet will not be propagated to SomeOtherComponent.
<MuiComponent component={SomeOtherComponent} as="button" />
AppBar
Remove z-index when position static and relative. This avoids the creation of a stacking context and rendering issues.
The
colorprop has no longer any effect in dark mode. The app bar uses the background color required by the elevation to follow the Material Design guidelines. UseenableColorOnDarkto restore the behavior of v4.<AppBar enableColorOnDark />
Alert
Move the component from the lab to the core. The component is now stable.
✅ This is handled in 🪄preset-safe codemod.
-import Alert from '@material-ui/lab/Alert'; -import AlertTitle from '@material-ui/lab/AlertTitle'; +import Alert from '@material-ui/core/Alert'; +import AlertTitle from '@material-ui/core/AlertTitle';
Autocomplete
Move the component from the lab to the core. The component is now stable.
✅ This is handled in 🪄preset-safe codemod.
-import Autocomplete from '@material-ui/lab/Autocomplete'; -import useAutocomplete from '@material-ui/lab/useAutocomplete'; +import Autocomplete from '@material-ui/core/Autocomplete'; +import useAutoComplete from '@material-ui/core/useAutocomplete';Remove
debugprop. There are a couple of simpler alternatives:open={true}, Chrome devtools "Emulate focused", or React devtools prop setter.renderOptionshould now return the full DOM structure of the option. It makes customizations easier. You can recover from the change with:<Autocomplete - renderOption={(option, { selected }) => ( - <React.Fragment> + renderOption={(props, option, { selected }) => ( + <li {...props}> <Checkbox icon={icon} checkedIcon={checkedIcon} style={{ marginRight: 8 }} checked={selected} /> {option.title} - </React.Fragment> + </li> )} />Rename
closeIconprop toclearIconto avoid confusion.✅ This is handled in 🪄preset-safe codemod.
-<Autocomplete closeIcon={defaultClearIcon} /> +<Autocomplete clearIcon={defaultClearIcon} />The following values of the reason argument in
onChangeandonClosewere renamed for consistency:create-optiontocreateOptionselect-optiontoselectOptionremove-optiontoremoveOption
Change the CSS rules that use
[data-focus="true"]to use.Mui-focused. Thedata-focusattribute is not set on the focused option anymore, instead, global class names are used.-'.MuiAutocomplete-option[data-focus="true"]': { +'.MuiAutocomplete-option.Mui-focused': {Rename
getOptionSelectedtoisOptionEqualToValueto better describe its purpose.✅ This is handled in 🪄preset-safe codemod.
<Autocomplete - getOptionSelected={(option, value) => option.title === value.title} + isOptionEqualToValue={(option, value) => option.title === value.title}
Avatar
Rename
circletocircularfor consistency:✅ This is handled in 🪄preset-safe codemod.
-<Avatar variant="circle"> -<Avatar classes={{ circle: 'className' }}> +<Avatar variant="circular"> +<Avatar classes={{ circular: 'className' }}>Since
circularis the default value, the variant prop can be deleted:-<Avatar variant="circle"> +<Avatar>Move the AvatarGroup from the lab to the core.
✅ This is handled in 🪄preset-safe codemod.
-import AvatarGroup from '@material-ui/lab/AvatarGroup'; +import AvatarGroup from '@material-ui/core/AvatarGroup';
Badge
Rename
circletocircularandrectangletorectangularfor consistency.✅ This is handled in 🪄preset-safe codemod.
-<Badge overlap="circle"> -<Badge overlap="rectangle"> +<Badge overlap="circular"> +<Badge overlap="rectangular"><Badge classes={{ - anchorOriginTopRightRectangle: 'className', - anchorOriginBottomRightRectangle: 'className', - anchorOriginTopLeftRectangle: 'className', - anchorOriginBottomLeftRectangle: 'className', - anchorOriginTopRightCircle: 'className', - anchorOriginBottomRightCircle: 'className', - anchorOriginTopLeftCircle: 'className', + anchorOriginTopRightRectangular: 'className', + anchorOriginBottomRightRectangular: 'className', + anchorOriginTopLeftRectangular: 'className', + anchorOriginBottomLeftRectangular: 'className', + anchorOriginTopRightCircular: 'className', + anchorOriginBottomRightCircular: 'className', + anchorOriginTopLeftCircular: 'className', }}>
BottomNavigation
TypeScript: The
eventinonChangeis no longer typed as aReact.ChangeEventbutReact.SyntheticEvent.-<BottomNavigation onChange={(event: React.ChangeEvent<{}>) => {}} /> +<BottomNavigation onChange={(event: React.SyntheticEvent) => {}} />
BottomNavigationAction
Remove the
spanelement that wraps the children. Remove thewrapperclassKey too. More details about this change.<button class="MuiBottomNavigationAction-root"> - <span class="MuiBottomNavigationAction-wrapper"> {icon} <span class="MuiBottomNavigationAction-label"> {label} </span> - </span> </button>
Box
The
borderRadiussystem prop value transformation has been changed. If it receives a number, it multiplies this value with thetheme.shape.borderRadiusvalue. Use a string to provide an explicitpxvalue.✅ This is handled in 🪄preset-safe codemod.
-<Box borderRadius="borderRadius"> +<Box borderRadius={1}>-<Box borderRadius={16}> +<Box borderRadius="16px">The Box system props have an optional alternative API in v5, using the
sxprop. You can read this section for the "why" behind this new API.✅ This is handled in 🪄preset-safe codemod.
<Box border="1px dashed grey" p={[2, 3, 4]} m={2}> <Box sx={{ border: "1px dashed grey", p: [2, 3, 4], m: 2 }}>The following properties have been renamed because they are considered deprecated CSS properties by the CSS specification:
✅ This is handled in 🪄preset-safe codemod.
gridGaptogapgridColumnGaptocolumnGapgridRowGaptorowGap
-<Box gridGap={1}> -<Box gridColumnGap={2}> -<Box gridRowGap={3}> +<Box gap={1}> +<Box columnGap={2}> +<Box rowGap={3}>(Note that the system grid function wasn't documented in v4.)
The
cloneprop was removed because its behavior can be obtained by applying thesxprop directly to the child if it is a Material-UI component.-<Box sx={{ border: '1px dashed grey' }} clone> - <Button>Save</Button> -</Box> +<Button sx={{ border: '1px dashed grey' }}>Save</Button>The ability to pass a render prop was removed because its behavior can be obtained by applying the
sxprop directly to the child if it is a Material-UI component.-<Box sx={{ border: '1px dashed grey' }}> - {(props) => <Button {...props}>Save</Button>} -</Box> +<Button sx={{ border: '1px dashed grey' }}>Save</Button>For non-Material-UI components, use the
componentprop.-<Box sx={{ border: '1px dashed grey' }}> - {(props) => <button {...props}>Save</button>} -</Box> +<Box component="button" sx={{ border: '1px dashed grey' }}>Save</Box>
Button
The button
colorprop is now "primary" by default, and "default" has been removed. This makes the button closer to the Material Design guidelines and simplifies the API.✅ This is handled in 🪄preset-safe codemod.
-<Button color="default"> +<Button>spanelement that wraps children has been removed.labelclassKey is also removed. More details about this change.<button class="MuiButton-root"> - <span class="MuiButton-label"> children - </span> </button>
Chip
Rename
defaultvariant tofilledfor consistency.✅ This is handled in 🪄preset-safe codemod.
Since
filledis the default value, the variant prop can be deleted:-<Chip variant="default"> +<Chip>
Checkbox
The component doesn't have
.MuiIconButton-rootand.MuiIconButton-labelclass names anymore, target.MuiButtonBase-rootinstead.- <span class="MuiIconButton-root MuiButtonBase-root MuiCheckbox-root PrivateSwitchBase-root"> - <span class="MuiIconButton-label"> - <input class="PrivateSwitchBase-input"> + <span class="MuiButtonBase-root MuiCheckbox-root PrivateSwitchBase-root"> + <span class="PrivateSwitchBase-input">
CircularProgress
The
staticvariant has been renamed todeterminate, and the previous appearance ofdeterminatehas been replaced by that ofstatic. It was an exception to Material Design, and was removed from the specification.✅ This is handled in 🪄preset-safe codemod.
-<CircularProgress variant="static" classes={{ static: 'className' }} /> +<CircularProgress variant="determinate" classes={{ determinate: 'className' }} />
NB: If you had previously customized determinate, your customizations are probably no longer valid. Please remove them.
Collapse
The
collapsedHeightprop was renamedcollapsedSizeto support the horizontal direction.✅ This is handled in 🪄preset-safe codemod.
-<Collapse collapsedHeight={40}> +<Collapse collapsedSize={40}>The
classes.containerkey was changed to match the convention of the other components.-<Collapse classes={{ container: 'collapse' }}> +<Collapse classes={{ root: 'collapse' }}>
CssBaseline
The component was migrated to use the
@material-ui/styled-engine(emotionorstyled-components) instead ofjss. You should remove the@globalkey when defining the style overrides for it. You could also start using the CSS template syntax over the JavaScript object syntax.const theme = createTheme({ components: { MuiCssBaseline: { - styleOverrides: { - '@global': { - html: { - WebkitFontSmoothing: 'auto', - }, - }, - }, + styleOverrides: ` + html { + -webkit-font-smoothing: auto; + } + ` }, }, });The
bodyfont size has changed fromtheme.typography.body2(0.875rem) totheme.typography.body1(1rem). To return to the previous size, you can override it in the theme:const theme = createTheme({ typography: { body1: { fontSize: '0.875rem', }, }, });(Note that this will also affect use of the Typography component with the default
body1variant).
Dialog
The onE* transition props were removed. Use TransitionProps instead.
✅ This is handled in 🪄preset-safe codemod.
<Dialog - onEnter={onEnter} - onEntered={onEntered} - onEntering={onEntering} - onExit={onExit} - onExited={onExited} - onExiting={onExiting} + TransitionProps={{ + onEnter, + onEntered, + onEntering, + onExit, + onExited, + onExiting, + }} >Remove the
disableBackdropClickprop because it is redundant. Ignore close events fromonClosewhenreason === 'backdropClick'instead.✅ This is handled in 🪄preset-safe codemod.
<Dialog - disableBackdropClick - onClose={handleClose} + onClose={(event, reason) => { + if (reason !== 'backdropClick') { + onClose(event, reason); + } + }} />Remove the
withMobileDialoghigher-order component. The hook API allows a simpler and more flexible solution:✅ This is handled in 🪄preset-safe codemod by applying hard-coded function to prevent application crash, further fixes are required.
-import withMobileDialog from '@material-ui/core/withMobileDialog'; +import { useTheme, useMediaQuery } from '@material-ui/core'; function ResponsiveDialog(props) { - const { fullScreen } = props; + const theme = useTheme(); + const fullScreen = useMediaQuery(theme.breakpoints.down('sm')); const [open, setOpen] = React.useState(false); // ... -export default withMobileDialog()(ResponsiveDialog); +export default ResponsiveDialog;Flatten DialogTitle DOM structure, remove
disableTypographyprop✅ This is handled in 🪄preset-safe codemod.
-<DialogTitle disableTypography> - <Typography variant="h4" component="h2"> +<DialogTitle> + <Typography variant="h4" component="span"> My header </Typography>
Divider
Use border instead of background color. It prevents inconsistent height on scaled screens. If you have customized the color of the border, you will need to update the CSS property override:
.MuiDivider-root { - background-color: #f00; + border-color: #f00; }
ExpansionPanel
Rename the
ExpansionPanelcomponents toAccordionto use a more common naming convention:✅ This is handled in 🪄preset-safe codemod.
-import ExpansionPanel from '@material-ui/core/ExpansionPanel'; -import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary'; -import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails'; -import ExpansionPanelActions from '@material-ui/core/ExpansionPanelActions'; +import Accordion from '@material-ui/core/Accordion'; +import AccordionSummary from '@material-ui/core/AccordionSummary'; +import AccordionDetails from '@material-ui/core/AccordionDetails'; +import AccordionActions from '@material-ui/core/AccordionActions'; -<ExpansionPanel> +<Accordion> - <ExpansionPanelSummary> + <AccordionSummary> <Typography>Location</Typography> <Typography>Select trip destination</Typography> - </ExpansionPanelSummary> + </AccordionSummary> - <ExpansionPanelDetails> + <AccordionDetails> <Chip label="Barbados" onDelete={() => {}} /> <Typography variant="caption">Select your destination of choice</Typography> - </ExpansionPanelDetails> + </AccordionDetails> <Divider /> - <ExpansionPanelActions> + <AccordionActions> <Button size="small">Cancel</Button> <Button size="small">Save</Button> - </ExpansionPanelActions> + </AccordionActions> -</ExpansionPanel> +</Accordion>TypeScript: The
eventinonChangeis no longer typed as aReact.ChangeEventbutReact.SyntheticEvent.-<Accordion onChange={(event: React.ChangeEvent<{}>, expanded: boolean) => {}} /> +<Accordion onChange={(event: React.SyntheticEvent, expanded: boolean) => {}} />
ExpansionPanelDetails
- Remove
display: flexfromAccordionDetails(formerlyExpansionPanelDetails) as its too opinionated. Most developers expect a display block.
ExpansionPanelSummary
Rename
focusedtofocusVisiblefor consistency:<AccordionSummary classes={{ - focused: 'custom-focus-visible-classname', + focusVisible: 'custom-focus-visible-classname', }} />Remove
IconButtonPropsprop fromAccordionSummary(formerlyExpansionPanelSummary). The component renders a<div>element instead of anIconButton. The prop is no longer necessary.
Fab
Rename
roundtocircularfor consistency:✅ This is handled in 🪄preset-safe codemod.
-<Fab variant="round"> +<Fab variant="circular">spanelement that wraps children has been removed.labelclassKey is also removed. More details about this change.<button class="MuiFab-root"> - <span class="MuiFab-label"> {children} - </span> </button>
FormControl
Change the default variant from
standardtooutlined. Standard has been removed from the Material Design guidelines.✅ This is handled in variant-prop codemod, read the details before running this codemod.
-<FormControl value="Standard" /> -<FormControl value="Outlined" variant="outlined" /> +<FormControl value="Standard" variant="standard" /> +<FormControl value="Outlined" />
FormControlLabel
- The
labelprop is now required. If you were using aFormControlLabelwithout alabel, you can replace it with just the value of thecontrolprop.
-<FormControlLabel control={<Checkbox />} />
+<Checkbox />
Grid
Rename
justifyprop tojustifyContentto align with the CSS property name.✅ This is handled in 🪄preset-safe codemod.
-<Grid justify="center"> +<Grid justifyContent="center">The props:
alignItemsalignContentandjustifyContentand theirclassesand style overrides keys were removed: "align-items-xs-center", "align-items-xs-flex-start", "align-items-xs-flex-end", "align-items-xs-baseline", "align-content-xs-center", "align-content-xs-flex-start", "align-content-xs-flex-end", "align-content-xs-space-between", "align-content-xs-space-around", "justify-content-xs-center", "justify-content-xs-flex-end", "justify-content-xs-space-between", "justify-content-xs-space-around" and "justify-content-xs-space-evenly". These props are now considered part of the system, not on theGridcomponent itself. If you still wish to add overrides for them, you can use thetheme.components.MuiGrid.variantsoptions.✅ This is handled in 🪄preset-safe codemod.
const theme = createTheme({ components: { MuiGrid: { - styleOverrides: { - "align-items-xs-flex-end": { - marginTop: '20px', - }, - }, + variants: { + props: { alignItems: "flex-end" }, + style: { + marginTop: '20px', + }, + }], }, }, });
GridList
Rename the
GridListcomponents toImageListto align with the current Material Design naming.✅ This is handled in 🪄preset-safe codemod.
Rename the GridList
spacingprop togapto align with the CSS attribute.Rename the GridList
cellHeightprop torowHeight.Add the
variantprop to GridList.Rename the GridListItemBar
actionPositionprop toposition. (Note also the related classname changes.)Use CSS object-fit. For IE11 support either use a polyfill such as https://www.npmjs.com/package/object-fit-images, or continue to use the v4 component.
-import GridList from '@material-ui/core/GridList'; -import GridListTile from '@material-ui/core/GridListTile'; -import GridListTileBar from '@material-ui/core/GridListTileBar'; +import ImageList from '@material-ui/core/ImageList'; +import ImageListItem from '@material-ui/core/ImageListItem'; +import ImageListItemBar from '@material-ui/core/ImageListItemBar'; -<GridList spacing={8} cellHeight={200}> - <GridListTile> +<ImageList gap={8} rowHeight={200}> + <ImageListItem> <img src="file.jpg" alt="Image title" /> - <GridListTileBar + <ImageListItemBar title="Title" subtitle="Subtitle" /> - </GridListTile> -</GridList> + </ImageListItem> +</ImageList>
Hidden
This component is deprecated because its functionality can be created with the
sxprop or theuseMediaQueryhook.✅ This is handled in 🪄preset-safe codemod by applying fake
Hiddencomponent to prevent application crash, further fixes are required.Use the
sxprop to replaceimplementation="css":-<Hidden implementation="css" xlUp><Paper /></Hidden> -<Hidden implementation="css" xlUp><button /></Hidden> +<Paper sx={{ display: { xl: 'none', xs: 'block' } }} /> +<Box component="button" sx={{ display: { xl: 'none', xs: 'block' } }} />-<Hidden implementation="css" mdDown><Paper /></Hidden> -<Hidden implementation="css" mdDown><button /></Hidden> +<Paper sx={{ display: { xs: 'none', md: 'block' } }} /> +<Box component="button" sx={{ display: { xs: 'none', md: 'block' } }} />Use the
useMediaQueryhook to replaceimplementation="js":-<Hidden implementation="js" xlUp><Paper /></Hidden> +const hidden = useMediaQuery(theme => theme.breakpoints.up('xl')); +return hidden ? null : <Paper />;
Icon
The default value of
fontSizewas changed fromdefaulttomediumfor consistency. In the unlikely event that you were using the valuedefault, the prop can be removed:-<Icon fontSize="default">icon-name</Icon> +<Icon>icon-name</Icon>
IconButton
The default size's padding is reduced to
8pxwhich makes the default IconButton size of40px. To get the old default size (48px), usesize="large". The change was done to better match Google's products when Material Design stopped documenting the icon button pattern.✅ This is handled in 🪄preset-safe codemod.
- <IconButton> + <IconButton size="large">spanelement that wraps children has been removed.labelclassKey is also removed. More details about this change.<button class="MuiIconButton-root"> - <span class="MuiIconButton-label"> <svg /> - </span> </button>
Link
The default
underlineprop is changed from"hover"to"always". To get the same behavior as in v4, applydefaultPropsin theme✅ This is handled in link-underline-hover codemod, read the details before running this codemod.
createTheme({ components: { MuiLink: { defaultProps: { underline: 'hover', }, }, }, });
Menu
The onE* transition props were removed. Use TransitionProps instead.
✅ This is handled in 🪄preset-safe codemod.
<Menu - onEnter={onEnter} - onEntered={onEntered} - onEntering={onEntering} - onExit={onExit} - onExited={onExited} - onExiting={onExiting} + TransitionProps={{ + onEnter, + onEntered, + onEntering, + onExit, + onExited, + onExiting, + }} >Note: The
selectedMenuvariant will no longer vertically align the selected item with the anchor.Change the default value of
anchorOrigin.verticalto follow the Material Design guidelines. The menu now displays below the anchor instead of on top of it. You can restore the previous behavior with:<Menu + anchorOrigin={{ + vertical: 'top', + horizontal: 'left', + }}
MenuItem
The
MenuItemcomponent inherits theButtonBasecomponent instead ofListItem. The class names related to "MuiListItem-*" are removed and themingListItemis no longer affectingMenuItem.-<li className="MuiButtonBase-root MuiMenuItem-root MuiListItem-root"> +<li className="MuiButtonBase-root MuiMenuItem-root">prop
listItemClassesis removed, useclassesinstead.-<MenuItem listItemClasses={{...}}> +<MenuItem classes={{...}}>Read more about MenuItem CSS API
Modal
Remove the
disableBackdropClickprop because it is redundant. UseonClosewithreason === 'backdropClick'instead.✅ This is handled in 🪄preset-safe codemod.
<Modal - disableBackdropClick - onClose={handleClose} + onClose={(event, reason) => { + if (reason !== 'backdropClick') { + onClose(event, reason); + } + }} />Remove the
onEscapeKeyDownprop because it is redundant. UseonClosewithreason === "escapeKeyDown"instead.✅ This is handled in 🪄preset-safe codemod.
<Modal - onEscapeKeyDown={handleEscapeKeyDown} + onClose={(event, reason) => { + if (reason === 'escapeKeyDown') { + handleEscapeKeyDown(event); + } + }} />Remove
onRenderedprop. Depending on your use case either use a callback ref on the child element or an effect hook in the child component.
NativeSelect
Merge the
selectMenuslot intoselect. SlotselectMenuwas redundant. Therootslot is no longer applied to the select, but to the root.-<NativeSelect classes={{ root: 'class1', select: 'class2', selectMenu: 'class3' }} /> +<NativeSelect classes={{ select: 'class1 class2 class3' }} />
OutlinedInput
Remove the
labelWidthprop. Thelabelprop now fulfills the same purpose, using CSS layout instead of JavaScript measurement to render the gap in the outlined.-<OutlinedInput labelWidth={20} /> +<OutlinedInput label="First Name" />
Paper
Change the background opacity based on the elevation in dark mode. This change was done to follow the Material Design guidelines. You can revert it in the theme:
const theme = createTheme({ components: { MuiPaper: { + styleOverrides: { root: { backgroundImage: 'unset' } }, }, }, });
Pagination
Move the component from the lab to the core. The component is now stable.
✅ This is handled in 🪄preset-safe codemod.
-import Pagination from '@material-ui/lab/Pagination'; -import PaginationItem from '@material-ui/lab/PaginationItem'; -import { usePagination } from '@material-ui/lab/Pagination'; +import Pagination from '@material-ui/core/Pagination'; +import PaginationItem from '@material-ui/core/PaginationItem'; +import usePagination from '@material-ui/core/usePagination';Rename
roundtocircularfor consistency:✅ This is handled in 🪄preset-safe codemod.
-<Pagination shape="round"> -<PaginationItem shape="round"> +<Pagination shape="circular"> +<PaginationItem shape="circular">
Popover
The onE* transition props were removed. Use TransitionProps instead.
✅ This is handled in 🪄preset-safe codemod.
<Popover - onEnter={onEnter} - onEntered={onEntered} - onEntering={onEntering} - onExit={onExit} - onExited={onExited} - onExiting={onExiting} + TransitionProps={{ + onEnter, + onEntered, + onEntering, + onExit, + onExited, + onExiting, + }} >The
getContentAnchorElprop was removed to simplify the positioning logic.
Popper
Upgrade Popper.js from v1 to v2. This third-party library has introduced a lot of changes.
You can read their migration guide or the following summary:The CSS prefixes have changed:
popper: { zIndex: 1, - '&[x-placement*="bottom"] .arrow': { + '&[data-popper-placement*="bottom"] .arrow': {Method names have changed:
-popperRef.current.scheduleUpdate() +popperRef.current.update()-popperRef.current.update() +popperRef.current.forceUpdate()Modifiers' API has changed a lot. There are too many changes to be covered here.
Portal
- Remove
onRenderedprop. Depending on your use case either use a callback ref on the child element or an effect hook in the child component.
Radio
The radio color prop is now "primary" by default. To continue using the "secondary" color, you must explicitly indicate
secondary. This brings the radio closer to the Material Design guidelines.-<Radio /> +<Radio color="secondary />The component doesn't have
.MuiIconButton-rootand.MuiIconButton-labelclass names anymore, target.MuiButtonBase-rootinstead.- <span class="MuiIconButton-root MuiButtonBase-root MuiRadio-root PrivateSwitchBase-root"> - <span class="MuiIconButton-label"> - <input class="PrivateSwitchBase-input"> + <span class="MuiButtonBase-root MuiRadio-root PrivateSwitchBase-root"> + <span class="PrivateSwitchBase-input">
Rating
Move the component from the lab to the core. The component is now stable.
✅ This is handled in 🪄preset-safe codemod.
-import Rating from '@material-ui/lab/Rating'; +import Rating from '@material-ui/core/Rating';Change the default empty icon to improve accessibility. If you have a custom
iconprop but noemptyIconprop, you can restore the previous behavior with:<Rating icon={customIcon} + emptyIcon={null} />Rename
visuallyhiddentovisuallyHiddenfor consistency:<Rating classes={{ - visuallyhidden: 'custom-visually-hidden-classname', + visuallyHidden: 'custom-visually-hidden-classname', }} />
RootRef
This component was removed. You can get a reference to the underlying DOM node of our components via
refprop. The component relied onReactDOM.findDOMNodewhich is deprecated inReact.StrictMode.✅ This is handled in 🪄preset-safe codemod by applying fake
RootRefcomponent to prevent application crash, further fixes are required.-<RootRef rootRef={ref}> - <Button /> -</RootRef> +<Button ref={ref} />
Select
Change the default variant from
standardtooutlined. Standard has been removed from the Material Design guidelines. If you are composing the Select with a form control component, you only need to updateFormControl, the select inherits the variant from its context.✅ This is handled in variant-prop codemod, read the details before running this codemod.
-<Select value="Standard" /> -<Select value="Outlined" variant="outlined" /> +<Select value="Standard" variant="standard" /> +<Select value="Outlined" />Remove the
labelWidthprop. Thelabelprop now fulfills the same purpose, using CSS layout instead of JavaScript measurement to render the gap in the outlined. The TextField already handles it by default.-<Select variant="outlined" labelWidth={20} /> +<Select variant="outlined" label="Gender" />Merge the
selectMenuslot intoselect. SlotselectMenuwas redundant. Therootslot is no longer applied to the select, but to the root.-<Select classes={{ root: 'class1', select: 'class2', selectMenu: 'class3' }} /> +<Select classes={{ select: 'class1 class2 class3' }} />The
eventinonChangeis now a synthetic, nativeEventnot a React event.-<Select onChange={(event: React.SyntheticEvent, value: unknown) => {}} /> +<Select onChange={(event: Event, value: unknown) => {}} />This was necessary to prevent overriding of
event.targetof the events that caused the change.
Skeleton
Move the component from the lab to the core. The component is now stable.
✅ This is handled in 🪄preset-safe codemod.
-import Skeleton from '@material-ui/lab/Skeleton'; +import Skeleton from '@material-ui/core/Skeleton';Rename
circletocircularandrecttorectangularfor consistency:✅ This is handled in 🪄preset-safe codemod.
-<Skeleton variant="circle" /> -<Skeleton variant="rect" /> -<Skeleton classes={{ circle: 'custom-circle-classname', rect: 'custom-rect-classname', }} /> +<Skeleton variant="circular" /> +<Skeleton variant="rectangular" /> +<Skeleton classes={{ circular: 'custom-circle-classname', rectangular: 'custom-rect-classname', }} />
Slider
The
eventinonChangeis now a synthetic, nativeEventnot a React event.-<Slider onChange={(event: React.SyntheticEvent, value: unknown) => {}} /> +<Slider onChange={(event: Event, value: unknown) => {}} />This was necessary to prevent overriding of
event.targetof the events that caused the change.The
ValueLabelComponentandThumbComponentprop is now part of thecomponentsprop.<Slider - ValueLabelComponent={CustomValueLabel} - ThumbComponent={CustomThumb} + components={{ + ValueLabel: CustomValueLabel, + Thumb: CustomThumb, + }} />Rework the CSS to match the latest Material Design guidelines and make custom styles more intuitive. See documentation.
You can reduce the density of the slider, closer to v4 with the
size="small"prop.
Snackbar
The notification now displays at the bottom left on large screens. This better matches the behavior of Gmail, Google Keep, material.io, etc. You can restore the previous behavior with:
-<Snackbar /> +<Snackbar anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} />The onE* transition props were removed. Use TransitionProps instead.
✅ This is handled in 🪄preset-safe codemod.
<Snackbar - onEnter={onEnter} - onEntered={onEntered} - onEntering={onEntering} - onExit={onExit} - onExited={onExited} - onExiting={onExiting} + TransitionProps={{ + onEnter, + onEntered, + onEntering, + onExit, + onExited, + onExiting, + }} >
SpeedDial
Move the component from the lab to the core. The component is now stable.
✅ This is handled in 🪄preset-safe codemod.
-import SpeedDial from '@material-ui/lab/SpeedDial'; -import SpeedDialAction from '@material-ui/lab/SpeedDialAction'; -import SpeedDialIcon from '@material-ui/lab/SpeedDialIcon'; +import SpeedDial from '@material-ui/core/SpeedDial'; +import SpeedDialAction from '@material-ui/core/SpeedDialAction'; +import SpeedDialIcon from '@material-ui/core/SpeedDialIcon';
Stepper
The root component (Paper) was replaced with a div. Stepper no longer has elevation, nor inherits Paper's props. This change is meant to encourage composition.
+<Paper square elevation={2}> - <Stepper elevation={2}> + <Stepper> <Step> <StepLabel>Hello world</StepLabel> </Step> </Stepper> +<Paper>Remove the built-in 24px padding.
-<Stepper> +<Stepper style={{ padding: 24 }}> <Step> <StepLabel>Hello world</StepLabel> </Step> </Stepper>
SvgIcon
The default value of
fontSizewas changed fromdefaulttomediumfor consistency. In the unlikey event that you were using the valuedefault, the prop can be removed:-<SvgIcon fontSize="default"> +<SvgIcon> <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" /> </SvgIcon>
Switch
Deprecate the second argument from
onChange. You can pull out the checked state by accessingevent.target.checked.function MySwitch() { - const handleChange = (event: React.ChangeEvent<HTMLInputElement>, checked: boolean) => { + const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { + const checked = event.target.checked; }; return <Switch onChange={handleChange} />; }The switch color prop is now "primary" by default. To continue using the "secondary" color, you must explicitly indicate
secondary. This brings the switch closer to the Material Design guidelines.-<Switch /> +<Switch color="secondary" />The component doesn't have
.MuiIconButton-rootand.MuiIconButton-labelclass names anymore, target.MuiButtonBase-rootinstead.<span class="MuiSwitch-root"> - <span class="MuiIconButton-root MuiButtonBase-root MuiSwitch-switchBase PrivateSwitchBase-root"> - <span class="MuiIconButton-label"> - <input class="MuiSwitch-input PrivateSwitchBase-input"> + <span class="MuiButtonBase-root MuiSwitch-switchBase PrivateSwitchBase-root"> + <span class="MuiSwitch-input PrivateSwitchBase-input">
Table
Rename the
defaultvalue of thepaddingprop tonormal.-<Table padding="default" /> -<TableCell padding="default" /> +<Table padding="normal" /> +<TableCell padding="normal" />
TablePagination
The customization of the table pagination's actions labels must be done with the
getItemAriaLabelprop. This increases consistency with thePaginationcomponent.<TablePagination - backIconButtonText="Avant" - nextIconButtonText="Après" + getItemAriaLabel={…}Rename
onChangeRowsPerPagetoonRowsPerPageChangeandonChangePagetoonPageChangedue to API consistency.✅ This is handled in 🪄preset-safe codemod.
<TablePagination - onChangeRowsPerPage={()=>{}} - onChangePage={()=>{}} + onRowsPerPageChange={()=>{}} + onPageChange={()=>{}}Separate classes for different table pagination labels. This allows simpler customizations.
<TablePagination - classes={{ caption: 'foo' }} + classes={{ selectLabel: 'foo', displayedRows: 'foo' }} />Move the custom class on
inputtoselect. Theinputkey is being applied on another element.<TablePagination - classes={{ input: 'foo' }} + classes={{ select: 'foo' }} />
Tabs
Change the default
indicatorColorandtextColorprop values to "primary". This is done to match the most common use cases with Material Design.-<Tabs /> +<Tabs indicatorColor="primary" textColor="inherit" />TypeScript: The
eventinonChangeis no longer typed as aReact.ChangeEventbutReact.SyntheticEvent.-<Tabs onChange={(event: React.ChangeEvent<{}>, value: unknown) => {}} /> +<Tabs onChange={(event: React.SyntheticEvent, value: unknown) => {}} />The API that controls the scroll buttons has been split it in two props.
- The
scrollButtonsprop controls when the scroll buttons are displayed depending on the space available. - The
allowScrollButtonsMobileprop removes the CSS media query that systematically hide the scroll buttons on mobile.
✅ This is handled in 🪄preset-safe codemod.
-<Tabs scrollButtons="on" /> -<Tabs scrollButtons="desktop" /> -<Tabs scrollButtons="off" /> +<Tabs scrollButtons allowScrollButtonsMobile /> +<Tabs scrollButtons /> +<Tabs scrollButtons={false} />- The
Tab
Tab
minWidthchanged from72px=>90px(without media-query) according to material-design specTab
maxWidthchanged from264px=>360pxaccording to material-design specspanelement that wraps children has been removed.wrapperclassKey is also removed. More details about this change.<button class="MuiTab-root"> - <span class="MuiTab-wrapper"> {icon} {label} - </span> </button>
TextField
Change the default variant from
standardtooutlined. Standard has been removed from the Material Design guidelines.✅ This is handled in variant-prop codemod, read the details before running this codemod.
-<TextField value="Standard" /> -<TextField value="Outlined" variant="outlined" /> +<TextField value="Standard" variant="standard" /> +<TextField value="Outlined" />Rename
rowsMaxprop withmaxRowsfor consistency with HTML attributes.✅ This is handled in 🪄preset-safe codemod.
-<TextField rowsMax={6}> +<TextField maxRows={6}>Better isolate the fixed textarea height behavior to the dynamic one. You need to use the
minRowsprop in the following case:✅ This is handled in 🪄preset-safe codemod.
-<TextField rows={2} maxRows={5} /> +<TextField minRows={2} maxRows={5} />Change ref forwarding expectations on custom
inputComponent. The component should forward therefprop instead of theinputRefprop.-function NumberFormatCustom(props) { - const { inputRef, onChange, ...other } = props; +const NumberFormatCustom = React.forwardRef(function NumberFormatCustom( + props, + ref, +) { const { onChange, ...other } = props; return ( <NumberFormat {...other} - getInputRef={inputRef} + getInputRef={ref}Rename
marginDenseandinputMarginDenseclasses tosizeSmallandinputSizeSmallto match the prop.-<Input margin="dense" /> +<Input size="small" />Set the InputAdornment
positionprop tostartorend. Usestartif used as the value of thestartAdornmentprop. Useendif used as the value of theendAdornmentprop.-<TextField startAdornment={<InputAdornment>kg</InputAdornment>} /> -<TextField endAdornment={<InputAdornment>kg</InputAdornment>} /> +<TextField startAdornment={<InputAdornment position="start">kg</InputAdornment>} /> +<TextField endAdornment={<InputAdornment position="end">kg</InputAdornment>} />
TextareaAutosize
Remove the
rowsprop, use theminRowsprop instead. This change aims to clarify the behavior of the prop.✅ This is handled in 🪄preset-safe codemod.
-<TextareaAutosize rows={2} /> +<TextareaAutosize minRows={2} />Rename
rowsMaxprop withmaxRowsfor consistency with HTML attributes.✅ This is handled in 🪄preset-safe codemod.
-<TextareAutosize rowsMax={6}> +<TextareAutosize maxRows={6}>Rename
rowsMinprop withminRowsfor consistency with HTML attributes.✅ This is handled in 🪄preset-safe codemod.
-<TextareAutosize rowsMin={1}> +<TextareAutosize minRows={1}>
ToggleButton
Move the component from the lab to the core. The component is now stable.
✅ This is handled in 🪄preset-safe codemod.
-import ToggleButton from '@material-ui/lab/ToggleButton'; -import ToggleButtonGroup from '@material-ui/lab/ToggleButtonGroup'; +import ToggleButton from '@material-ui/core/ToggleButton'; +import ToggleButtonGroup from '@material-ui/core/ToggleButtonGroup';spanelement that wraps children has been removed.labelclassKey is also removed. More details about this change.<button class="MuiToggleButton-root"> - <span class="MuiToggleButton-label"> {children} - </span> </button>
Tooltip
Tooltips are now interactive by default.
The previous default behavior failed success criterion 1.4.3 ("hoverable") in WCAG 2.1. To reflect the new default value, the prop was renamed to
disableInteractive. If you want to restore the old behavior (thus not reaching level AA), you can apply the following diff:-<Tooltip> +<Tooltip disableInteractive> # Interactive tooltips no longer need the `interactive` prop. -<Tooltip interactive> +<Tooltip>
Typography
Remove the
srOnlyvariant. You can use thevisuallyHiddenutility in conjunction with thesxprop instead.+import { visuallyHidden } from '@material-ui/utils'; -<Typography variant="srOnly">Create a user</Typography> +<span style={visuallyHidden}>Create a user</span>The following
classesand style overrides keys were removed: "colorInherit", "colorPrimary", "colorSecondary", "colorTextPrimary", "colorTextSecondary", "colorError", "displayInline" and "displayBlock". These props are now considered part of the system, not on theTypographycomponent itself. If you still wish to add overrides for them, you can use thetheme.components.MuiTypography.variantsoptions. For exampleconst theme = createTheme({ components: { MuiTypography: { - styleOverrides: { - colorSecondary: { - marginTop: '20px', - }, - }, + variants: { + props: { color: "secondary" }, + style: { + marginTop: '20px', + }, + }], }, }, });
Theme
The default background color is now
#fffin light mode and#121212in dark mode. This matches the Material Design guidelines.Breakpoints are now treated as values instead of ranges. The behavior of
down(key)was changed to define a media query below the value defined by the corresponding breakpoint (exclusive), rather than the breakpoint above.between(start, end)was also updated to define a media query for the values between the actual values of start (inclusive) and end (exclusive). When using thedown()breakpoints utility you need to update the breakpoint key by one step up. When using thebetween(start, end)the end breakpoint should also be updated by one step up.✅ This is handled in 🪄preset-safe codemod.
Here are some examples of the changes required:
-theme.breakpoints.down('sm') // '@media (max-width:959.95px)' - [0, sm + 1) => [0, md) +theme.breakpoints.down('md') // '@media (max-width:959.95px)' - [0, md)-theme.breakpoints.between('sm', 'md') // '@media (min-width:600px) and (max-width:1279.95px)' - [sm, md + 1) => [0, lg) +theme.breakpoints.between('sm', 'lg') // '@media (min-width:600px) and (max-width:1279.95px)' - [0, lg)-theme.breakpoints.between('sm', 'xl') // '@media (min-width:600px)' +theme.breakpoints.up('sm') // '@media (min-width:600px)'The same should be done when using the
Hiddencomponent:-<Hidden smDown>{...}</Hidden> // '@media (min-width:600px)' +<Hidden mdDown>{...}</Hidden> // '@media (min-width:600px)'The default breakpoints were changed to better match the common use cases. They also better match the Material Design guidelines. Read more about the change
{ xs: 0, sm: 600, - md: 960, + md: 900, - lg: 1280, + lg: 1200, - xl: 1920, + xl: 1536, }If you prefer the old breakpoint values, use the snippet below.
import { createTheme } from '@material-ui/core/styles'; const theme = createTheme({ breakpoints: { values: { xs: 0, sm: 600, md: 960, lg: 1280, xl: 1920, }, }, });
The
theme.breakpoints.widthutility was removed because it's redundant. Usetheme.breakpoints.valuesto get the same values.✅ This is handled in 🪄preset-safe codemod.
-theme.breakpoints.width('md') +theme.breakpoints.values.mdThe signature of
theme.palette.augmentColorhelper has changed:-theme.palette.augmentColor(red); +theme.palette.augmentColor({ color: red, name: 'brand' });The
theme.typography.roundhelper was removed because it was no longer used. If you need it, use the function below:✅ This is handled in 🪄preset-safe codemod.
function round(value) { return Math.round(value * 1e5) / 1e5; }
@material-ui/types
Rename the exported
Omittype in@material-ui/types. The module is now calledDistributiveOmit. The change removes the confusion with the built-inOmithelper introduced in TypeScript v3.5. The built-inOmit, while similar, is non-distributive. This leads to differences when applied to union types. See this StackOverflow answer for further details.-import { Omit } from '@material-ui/types'; +import { DistributiveOmit } from '@material-ui/types';
Migrate theme's styleOverrides to emotion
Although your style overrides defined in the theme may partially work, there is an important difference on how the nested elements are styled. The $ syntax used with JSS will not work with Emotion. You need to replace those selectors with a valid class selector.
Replace state class names
const theme = createTheme({
components: {
MuiOutlinedInput: {
styleOverrides: {
root: {
- '&$focused': {
+ '&.Mui-focused': {
borderWidth: 1,
}
}
}
}
}
});
Replace nested classes selectors with global class names
const theme = createTheme({
components: {
MuiOutlinedInput: {
styleOverrides: {
root: {
- '& $notchedOutline': {
+ '& .MuiOutlinedInput-notchedOutline': {
borderWidth: 1,
}
}
}
}
}
});
Note: For each component we export a
[component]Classesconstant that contains all nested classes for that component. You can rely on this instead of hardcoding the classes.
+import { outlinedInputClasses } from '@material-ui/core/OutlinedInput';
const theme = createTheme({
components: {
MuiOutlinedInput: {
styleOverrides: {
root: {
- '& $notchedOutline': {
+ [`& .${outlinedInputClasses['notchedOutline']}`]: {
borderWidth: 1,
}
}
}
}
}
});
Take a look at the whole list of global state classnames available.
Migrate makeStyles to emotion
This is the last step in the migration process to remove @material-ui/styles package from your codebase.
We recommend two options.
1. Use styled or sx API
Codemod
We provide a codemod to help migrate JSS styles to styled API, but this approach increases the CSS specificity.
npx @material-ui/codemod@next v5.0.0/jss-to-styled <path>
Example transformation:
import Typography from '@material-ui/core/Typography';
-import makeStyles from '@material-ui/styles/makeStyles';
+import { styled } from '@material-ui/core/styles';
-const useStyles = makeStyles((theme) => ({
- root: {
- display: 'flex',
- alignItems: 'center',
- backgroundColor: theme.palette.primary.main
- },
- cta: {
- borderRadius: theme.shape.radius.
- },
- content: {
- color: theme.palette.common.white,
- fontSize: 16,
- lineHeight: 1.7
- },
-}))
+const PREFIX = 'MyCard';
+const classes = {
+ root: `${PREFIX}-root`,
+ cta: `${PREFIX}-cta`,
+ content: `${PREFIX}-content`,
+}
+const Root = styled('div')(({ theme }) => ({
+ [`&.${classes.root}`]: {
+ display: 'flex',
+ alignItems: 'center',
+ backgroundColor: theme.palette.primary.main
+ },
+ [`& .${classes.cta}`]: {
+ borderRadius: theme.shape.radius.
+ },
+ [`& .${classes.content}`]: {
+ color: theme.palette.common.white,
+ fontSize: 16,
+ lineHeight: 1.7
+ },
+}))
export const MyCard = () => {
- const classes = useStyles();
return (
- <div className={classes.root}>
+ <Root className={classes.root}>
{/* The benefit of this approach is that the code inside Root stays the same. */}
<Typography className={classes.content}>...</Typography>
<Button className={classes.cta}>Go</Button>
- </div>
+ </Root>
)
}
💡 You should run this codemod per small chunk of files and then check the changes because in some cases you might need to adjust the code after the transformation (this codemod won't cover all of the cases).
We recommend sx API over styled when you have to create responsive styles or needs minor CSS overrides. Read more about sx.
import Chip from '@material-ui/core/Chip';
-import makeStyles from '@material-ui/styles/makeStyles';
+import { styled } from '@material-ui/core/styles';
-const useStyles = makeStyles((theme) => ({
- wrapper: {
- display: 'flex',
- },
- chip: {
- padding: theme.spacing(1, 1.5),
- boxShadow: theme.shadows[1],
- }
-}))
+const Root = styled('div')({
+ display: 'flex',
+})
function App() {
- const classes = useStyles();
return (
- <div>
- <Chip className={classes.chip} label="Chip" />
- </div>
+ <Root>
+ <Chip label="Chip" sx={{ py: 1, px: 1.5, boxShadow: 1 }} />
+ </Root>
)
}
Manual
In some cases, you might want to create multiple styled components in a file instead of increasing CSS specificity. for example:
-import makeStyles from '@material-ui/styles/makeStyles';
+import { styled } from '@material-ui/core/styles';
-const useStyles = makeStyles((theme) => ({
- root: {
- display: 'flex',
- alignItems: 'center',
- borderRadius: 20,
- background: theme.palette.grey[50],
- },
- label: {
- color: theme.palette.primary.main,
- }
-}))
+const Root = style('div')(({ theme }) => ({
+ display: 'flex',
+ alignItems: 'center',
+ borderRadius: 20,
+ background: theme.palette.grey[50],
+}))
+const Label = style('span')(({ theme }) => ({
+ color: theme.palette.primary.main,
+}))
function Status({ label }) {
const classes = useStyles();
return (
- <div className={classe.root}>
- {icon}
- <span className={classes.label}>{label}</span>
- </div>
+ <Root className={classe.root}>
+ {icon}
+ <Label className={classes.label}>{label}</Label>
+ </Root>
)
}
Note: https://siriwatk.dev/tool/jss-to-styled is a tool that helps converting JSS to multiple styled components without increasing CSS specificity. (This tool is not maintained by Material-UI)
2. Use tss-react
The API is similar to JSS makeStyles but work with emotion.
Note: this library is not maintained by Material-UI. If you have any issue regarding to it, please open an issue in tss-react repository.
💡 Once you migrate all of the styling, remove unnecessary @material-ui/styles by
npm uninstall @material-ui/styles
// or with `yarn`
yarn remove @material-ui/styles
Troubleshooting
Storybook emotion with v5
If your project use Storybook v6.x, you will need to update .storybook/main.js webpack config to use the most recent version of emotion.
// .storybook/main.js
const path = require('path');
const toPath = (filePath) => path.join(process.cwd(), filePath);
module.exports = {
webpackFinal: async (config) => {
return {
...config,
resolve: {
...config.resolve,
alias: {
...config.resolve.alias,
'@emotion/core': toPath('node_modules/@emotion/react'),
'emotion-theming': toPath('node_modules/@emotion/react'),
},
},
};
},
};
For more details, checkout this issue on GitHub.
Cannot read property scrollTop of null
This error comes from Fade, Grow, Slide, Zoom components due to missing DOM Node.
You need to make sure that the children forward ref to DOM for custom component.
// Ex. 1 ✅ html tag works since it is a DOM
<Fade in>
<div>
<CustomComponent />
</div>
</Fade>
// Ex. 2 ❌ This will cause error. don't use Fragment as a child
<Fade in>
<React.Fragment>
<CustomComponent />
</React.Fragment>
</Fade>;
// Ex. 3 ❌ This will cause error because `CustomComponent` does not forward ref to DOM
function CustomComponent() {
return <div>...</div>;
}
<Fade in>
<CustomComponent />
</Fade>;
// ✅ Fixed by using `React.forwardRef` and pass to DOM.
const CustomComponent = React.forwardRef(function CustomComponent(props, ref) {
return (
<div ref={ref}>
...
</div>
)
})
<Fade in>
<CustomComponent />
</Fade>
For more details, checkout this issue on GitHub.
[Types] Property "palette", "spacing" does not exist on type 'DefaultTheme'
Since makeStyles is now exported from @material-ui/styles package which does not know about Theme in the core package. To fix this, you need to augment the DefaultTheme (empty object) in @material-ui/styles with Theme from the core. Read more about module augmentation
Typescript Project
Put this snippet to your theme file:
// it could be your App.tsx file or theme file that is included in your tsconfig.json
import { Theme } from '@material-ui/core/styles';
declare module '@material-ui/styles/defaultTheme' {
// eslint-disable-next-line @typescript-eslint/no-empty-interface (remove this line if you don't have the rule enabled)
interface DefaultTheme extends Theme {}
}
Javascript Project
If your IDE (ex. VSCode) is able to infer types from d.ts file, create index.d.ts in your src folder with this snippet:
// index.d.ts
declare module "@material-ui/private-theming" {
import type { Theme } from "@material-ui/core/styles";
interface DefaultTheme extends Theme {}
}
[Jest] SyntaxError: Unexpected token 'export'
In v5, @material-ui/core/colors/red is considered private and should not be used in your project. More details about this error.
You can use this codemod (recommended) to fix all the import in your project:
npx @material-ui/codemod@next v5.0.0/optimal-imports <path>
or fix it manually like this:
-import red from '@material-ui/core/colors/red';
+import { red } from '@material-ui/core/colors';
makeStyles - TypeError: Cannot read property 'drawer' of undefined
This error occurs when calling useStyles (result of makeStyles) or withStyles outside of <ThemeProvider> scope like this:
import * as React from 'react';
import { ThemeProvider, createTheme } from '@material-ui/core/styles';
import makeStyles from '@material-ui/styles/makeStyles';
import Card from '@material-ui/core/Card';
import CssBaseline from '@material-ui/core/CssBaseline';
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
backgroundColor: theme.palette.primary.main,
color: theme.palette.common.white,
},
}));
const theme = createTheme();
function App() {
const classes = useStyles(); // ❌ called outside of ThemeProvider
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Card className={classes.root}>...</Card>
</ThemeProvider>
);
}
export default App;
You can fix by moving useStyles inside another component so that it is called under <ThemeProvider>.
// ...imports
function AppContent(props) {
const classes = useStyles(); // ✅ This is safe because it is called inside ThemeProvider
return <Card className={classes.root}>...</Card>;
}
function App(props) {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<AppContent {...props} />
</ThemeProvider>
);
}
export default App;
