migration
MUI Version Migration
v4 → v5 Migration
Package Renames
# Uninstall v4 packages
npm uninstall @material-ui/core @material-ui/icons @material-ui/lab @material-ui/system
# Install v5 packages
npm install @mui/material @mui/icons-material @mui/lab @mui/system
npm install @emotion/react @emotion/styled # new required peer deps
# If using styled-components instead of emotion (less common):
npm install @mui/material @mui/styled-engine-sc styled-components
Run the v5 Codemod
The codemod handles ~80% of the mechanical changes automatically.
# Run the v5 preset-safe codemod (safe subset — no breaking changes)
npx @mui/codemod v5.0.0/preset-safe src/
# Run on a single file
npx @mui/codemod v5.0.0/preset-safe src/components/MyComponent.tsx
# Run individual transforms
npx @mui/codemod v5.0.0/component-rename-prop src/
npx @mui/codemod v5.0.0/moved-lab-modules src/
npx @mui/codemod v5.0.0/optimal-imports src/
After running the codemod, review the diff carefully — some transformations may need manual adjustment.
Import Path Changes
// v4
import { makeStyles } from '@material-ui/core/styles';
import { createMuiTheme } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
// v5
import { styled } from '@mui/material/styles'; // replaces makeStyles
import { createTheme } from '@mui/material/styles'; // createMuiTheme → createTheme
import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField';
createTheme Changes
// v4
import { createMuiTheme } from '@material-ui/core/styles';
const theme = createMuiTheme({
palette: {
primary: { main: '#1976d2' },
},
overrides: { // v4: overrides
MuiButton: {
root: { textTransform: 'none' },
},
},
props: { // v4: props
MuiButton: { disableRipple: true },
},
});
// v5
import { createTheme } from '@mui/material/styles';
const theme = createTheme({
palette: {
primary: { main: '#1976d2' },
},
components: { // v5: components (combines overrides + props)
MuiButton: {
defaultProps: { // was: props
disableRipple: true,
},
styleOverrides: { // was: overrides
root: {
textTransform: 'none',
},
},
},
},
});
makeStyles → styled / sx
This is the most impactful change. JSS (makeStyles, withStyles) is replaced by Emotion.
Pattern 1: sx prop (simple, inline styles)
// v4 with makeStyles
const useStyles = makeStyles((theme) => ({
root: {
padding: theme.spacing(2),
backgroundColor: theme.palette.background.paper,
borderRadius: theme.shape.borderRadius,
},
}));
function MyComponent() {
const classes = useStyles();
return <div className={classes.root}>Content</div>;
}
// v5 with sx prop
function MyComponent() {
return (
<Box
sx={{
p: 2,
bgcolor: 'background.paper',
borderRadius: 1,
}}
>
Content
</Box>
);
}
Pattern 2: styled() (reusable styled components)
// v4 with makeStyles + clsx
const useStyles = makeStyles((theme) => ({
card: {
padding: theme.spacing(3),
border: `1px solid ${theme.palette.divider}`,
borderRadius: theme.shape.borderRadius * 2,
},
cardHighlighted: {
borderColor: theme.palette.primary.main,
backgroundColor: theme.palette.primary.light,
},
}));
function MyCard({ highlighted }: { highlighted: boolean }) {
const classes = useStyles();
return (
<div className={clsx(classes.card, { [classes.cardHighlighted]: highlighted })}>
Content
</div>
);
}
// v5 with styled()
import { styled } from '@mui/material/styles';
const StyledCard = styled('div', {
shouldForwardProp: (prop) => prop !== 'highlighted',
})<{ highlighted?: boolean }>(({ theme, highlighted }) => ({
padding: theme.spacing(3),
border: `1px solid ${theme.palette.divider}`,
borderRadius: theme.shape.borderRadius * 2,
...(highlighted && {
borderColor: theme.palette.primary.main,
backgroundColor: theme.palette.primary.light,
}),
}));
function MyCard({ highlighted }: { highlighted: boolean }) {
return <StyledCard highlighted={highlighted}>Content</StyledCard>;
}
Pattern 3: tss-react (drop-in makeStyles replacement)
For large codebases where a full migration is impractical, tss-react provides a near-identical API:
npm install tss-react @emotion/react
// Minimal diff from v4 makeStyles
import { makeStyles } from 'tss-react/mui';
const useStyles = makeStyles()((theme) => ({
root: {
padding: theme.spacing(2),
backgroundColor: theme.palette.background.paper,
},
}));
function MyComponent() {
const { classes } = useStyles();
return <div className={classes.root}>Content</div>;
}
withStyles → styled
// v4
const StyledButton = withStyles((theme) => ({
root: {
margin: theme.spacing(1),
},
label: {
color: theme.palette.primary.main,
},
}))(Button);
// v5
const StyledButton = styled(Button)(({ theme }) => ({
margin: theme.spacing(1),
'& .MuiButton-label': { // target internal class
color: theme.palette.primary.main,
},
}));
// Or better — use sx prop on Button directly
color Prop Changes
// v4 — color accepted any string
<Button color="default">Default</Button>
// v5 — 'default' removed; use 'inherit' or omit color
<Button>Default</Button>
<Button color="inherit">Inherit parent color</Button>
System Props Removed from Most Components
In v5, system props (like mt, p, bgcolor) work on Box and Stack but not on
all components. Use the sx prop instead.
// v4 — system props on Typography
<Typography mt={2} color="primary.main">Text</Typography>
// v5 — use sx prop
<Typography sx={{ mt: 2, color: 'primary.main' }}>Text</Typography>
Lab Component Moves
Several components graduated from @mui/lab to @mui/material:
| v4 lab | v5 core |
|---|---|
@material-ui/lab/Alert |
@mui/material/Alert |
@material-ui/lab/Autocomplete |
@mui/material/Autocomplete |
@material-ui/lab/Pagination |
@mui/material/Pagination |
@material-ui/lab/Rating |
@mui/material/Rating |
@material-ui/lab/Skeleton |
@mui/material/Skeleton |
@material-ui/lab/SpeedDial |
@mui/material/SpeedDial |
@material-ui/lab/ToggleButton |
@mui/material/ToggleButton |
v4 → v5 Breaking Changes Checklist
- Replace
@material-ui/*with@mui/* - Add
@emotion/reactand@emotion/styledpeer deps - Replace
createMuiThemewithcreateTheme - Replace
theme.overrides+theme.propswiththeme.components - Replace all
makeStyles/withStyles(JSS) withstyled/sx/tss-react - Remove
color="default"— usecolor="inherit"or omit - Update Lab imports to core for graduated components
- Replace
<Hidden>withsx={{ display: { xs: 'none', sm: 'block' } }} - Update
Boxsystem shorthand keys (some changed) - Check all
classNameoverrides — internal class names changed in v5
v5 → v6 Migration
Run the v6 Codemod
# Run preset-safe (recommended first step)
npx @mui/codemod v6.0.0/preset-safe src/
# Run on specific directories
npx @mui/codemod v6.0.0/preset-safe src/components/ src/pages/
# Individual transforms (if preset-safe missed something)
npx @mui/codemod v6.0.0/grid-v2-props src/
npx @mui/codemod v6.0.0/sx-prop src/
Grid v2 (Major Breaking Change)
Grid v2 is the default Grid in v6. The item prop is removed; use size instead.
The xs, sm, md, lg, xl props are replaced by a single size prop with an object.
// v5 Grid
import Grid from '@mui/material/Grid';
<Grid container spacing={2}>
<Grid item xs={12} sm={6} md={4}>
<Card />
</Grid>
<Grid item xs={12} sm={6} md={8}>
<Content />
</Grid>
</Grid>
// v6 Grid (Grid v2) — same import path, new API
import Grid from '@mui/material/Grid';
<Grid container spacing={2}>
<Grid size={{ xs: 12, sm: 6, md: 4 }}>
<Card />
</Grid>
<Grid size={{ xs: 12, sm: 6, md: 8 }}>
<Content />
</Grid>
</Grid>
// Shorthand for full-width only
<Grid size={12}>Full width</Grid>
// Auto-grow
<Grid size="grow">Fills remaining space</Grid>
Grid v2 — Offset
// v5 — offset via empty grid items or negative margins
<Grid item xs={4} /> // spacer
<Grid item xs={8}><Content /></Grid>
// v6 — offset prop
<Grid size={8} offset={4}><Content /></Grid>
// Responsive offset
<Grid size={{ xs: 12, md: 8 }} offset={{ md: 4 }}>
<Content />
</Grid>
Slots Pattern Standardized
In v6, the slots/slotProps API is standardized across all MUI X components
(DatePicker, DataGrid, Charts). The old components/componentsProps API is removed.
// v5 (deprecated in v6)
<DatePicker
components={{ TextField: CustomTextField }}
componentsProps={{ textField: { variant: 'outlined' } }}
/>
// v6 (required)
<DatePicker
slots={{ textField: CustomTextField }}
slotProps={{ textField: { variant: 'outlined' } }}
/>
Pigment CSS (Experimental in v6)
v6 introduces optional Pigment CSS — a build-time CSS extraction alternative to Emotion. It enables zero-runtime styling (faster SSR, smaller bundles).
# Install Pigment CSS (optional — still experimental in v6)
npm install @mui/material-pigment-css @pigment-css/react
# Next.js setup
npm install @pigment-css/nextjs-plugin
// next.config.js
const { withPigment } = require('@pigment-css/nextjs-plugin');
module.exports = withPigment({
// your next config
}, {
theme: require('./src/theme').default,
});
With Pigment CSS, styled() and sx are processed at build time — no Emotion runtime.
Server components can use styled components directly without 'use client'.
v5 → v6 Breaking Changes Checklist
- Update all packages:
npm install @mui/material@6 @mui/x-date-pickers@7 @mui/x-data-grid@7 - Replace Grid v1 props (
item,xs/sm/md) with Grid v2sizeprop - Replace
components/componentsPropswithslots/slotPropsin all MUI X components - Check
@mui/x-date-pickerspeer dep: now requiresdayjsor another adapter explicitly -
useMediaQuerynow requires athemeargument in some contexts — verify usages -
Stackdividerspacing changed — verify visual output - Review
@mui/x-data-gridAPI: some column definition fields changed
Incremental Migration Strategy
For large codebases (100+ components), do not migrate everything at once.
Phase 1: Package and Config (1 day)
# 1. Update packages
npm uninstall @material-ui/core @material-ui/icons
npm install @mui/material @mui/icons-material @emotion/react @emotion/styled
# 2. Run codemods
npx @mui/codemod v5.0.0/preset-safe src/
# 3. Fix obvious import errors
# 4. Run the app; expect red errors — that's normal at this stage
Phase 2: Theme Migration (1–2 days)
// Migrate theme file first — everything depends on it
// Old: createMuiTheme with overrides/props
// New: createTheme with components.MuiXxx.styleOverrides/defaultProps
Phase 3: Component-by-Component (ongoing)
Prioritize components that are:
- Used most frequently (Button, TextField, etc.)
- Already have a clear
sx-based replacement - Self-contained (not deeply composed)
// Migrate one component at a time:
// 1. Remove makeStyles import
// 2. Replace className={classes.xxx} with sx={...} or styled()
// 3. Run tests
// 4. Commit
Phase 4: Remove JSS Entirely
# Once all makeStyles are removed:
npm uninstall @material-ui/styles
Coexistence Strategy (v4 + v5 during migration)
// Temporarily keep both packages during migration
// @material-ui/core for unmigrated components
// @mui/material for migrated components
// Wrap each style engine separately
import { StylesProvider } from '@material-ui/core/styles'; // v4 JSS
import { ThemeProvider } from '@mui/material/styles'; // v5 Emotion
function App() {
return (
<StylesProvider injectFirst>
<ThemeProvider theme={v5Theme}>
{/* migrated components use v5 theme */}
{/* unmigrated components use v4 styles */}
<Router />
</ThemeProvider>
</StylesProvider>
);
}
makeStyles → styled/sx Conversion Reference
| makeStyles pattern | v5 equivalent |
|---|---|
padding: theme.spacing(2) |
sx={{ p: 2 }} |
margin: theme.spacing(1, 2) |
sx={{ my: 1, mx: 2 }} |
color: theme.palette.primary.main |
sx={{ color: 'primary.main' }} |
backgroundColor: theme.palette.grey[100] |
sx={{ bgcolor: 'grey.100' }} |
display: 'flex' |
sx={{ display: 'flex' }} |
[theme.breakpoints.up('sm')]: { ... } |
sx={{ md: { ... } }} |
'&:hover': { ... } |
sx={{ '&:hover': { ... } }} |
'& .MuiButton-root': { ... } |
sx={{ '& .MuiButton-root': { ... } }} |
theme.transitions.create('opacity') |
styled()(({ theme }) => ({ transition: theme.transitions.create('opacity') })) |
Common Post-Migration Issues
Styles not applying:
- Check that
CssBaselineis insideThemeProvider - Confirm
@emotion/reactand@emotion/styledare installed - Ensure no JSS
StylesProvideris conflicting
TypeScript errors after codemod:
makeStylesreturn type changed —classesis now in a{ classes }object with tss-reactThemeimport moved from@material-ui/coreto@mui/material/styles- Some prop types narrowed (e.g.,
colorno longer accepts'default')
SSR style flash (FOUC):
- Missing Emotion cache setup — see the performance skill for full SSR config
- Ensure
createEmotionCache()is called per request, not once globally
Grid layout broken after v6 upgrade:
- The
itemprop is removed — addsizeprop to every grid item - Run
npx @mui/codemod v6.0.0/grid-v2-props src/to automate this
More from lobbi-docs/claude
vision-multimodal
Vision and multimodal capabilities for Claude including image analysis, PDF processing, and document understanding. Activate for image input, base64 encoding, multiple images, and visual analysis.
242design-system
Apply and manage the AI-powered design system with 50+ curated styles
126complex-reasoning
Multi-step reasoning patterns and frameworks for systematic problem solving. Activate for Chain-of-Thought, Tree-of-Thought, hypothesis-driven debugging, and structured analytical approaches that leverage extended thinking.
105gcp
Google Cloud Platform services including GKE, Cloud Run, Cloud Storage, BigQuery, and Pub/Sub. Activate for GCP infrastructure, Google Cloud deployment, and GCP integration.
73kanban
Kanban methodology including boards, WIP limits, flow metrics, and continuous delivery. Activate for Kanban boards, workflow visualization, and lean project management.
62debugging
Debugging techniques for Python, JavaScript, and distributed systems. Activate for troubleshooting, error analysis, log investigation, and performance debugging. Includes extended thinking integration for complex debugging scenarios.
59