Some checks failed
No response / noResponse (push) Has been cancelled
CI / Continuous releases (push) Has been cancelled
CI / test-dev (macos-latest) (push) Has been cancelled
CI / test-dev (ubuntu-latest) (push) Has been cancelled
CI / test-dev (windows-latest) (push) Has been cancelled
Maintenance / main (push) Has been cancelled
Scorecards supply-chain security / Scorecards analysis (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
473 lines
15 KiB
Markdown
473 lines
15 KiB
Markdown
# Migrating from JSS (optional)
|
||
|
||
<p class="description">This guide explains how to migrate from JSS to Emotion when updating from Material UI v4 to v5.</p>
|
||
|
||
## Material UI v5 migration
|
||
|
||
1. [Getting started](/material-ui/migration/migration-v4/)
|
||
2. [Breaking changes part one: style and theme](/material-ui/migration/v5-style-changes/)
|
||
3. [Breaking changes part two: components](/material-ui/migration/v5-component-changes/)
|
||
4. Migrating from JSS 👈 _you are here_
|
||
5. [Troubleshooting](/material-ui/migration/troubleshooting/)
|
||
|
||
## Migrating from JSS to Emotion
|
||
|
||
One of the biggest changes in v5 is the replacement of JSS for [Emotion](https://emotion.sh/docs/introduction) (or [styled-components](https://styled-components.com/) as an alternative) as a default styling solution .
|
||
|
||
Note that you may continue to use JSS for adding overrides for the components (for example `makeStyles`, `withStyles`) even after migrating to v5.
|
||
Then, if at any point you want to move over to the new styling engine, you can refactor your components progressively.
|
||
|
||
:::info
|
||
If you are using Next.js and you are not sure how to configure SSR to work with both Emotion & JSS, take a look a this [example project](https://github.com/mui/material-ui/tree/master/examples/material-ui-nextjs-ts-v4-v5-migration).
|
||
:::
|
||
|
||
This document reviews all the steps necessary to migrate away from JSS.
|
||
|
||
While you can use either of the following two options, the first is considered preferable:
|
||
|
||
### 1. Use styled or sx API
|
||
|
||
#### Codemod
|
||
|
||
We provide [a codemod](https://github.com/mui/material-ui/blob/master/packages/mui-codemod/README.md#jss-to-styled) to help migrate JSS styles to `styled` API, but this approach increases the CSS specificity.
|
||
|
||
:::info
|
||
Normally you wouldn't write styles like this.
|
||
But this is the best transformation that we could create with a codemod.
|
||
|
||
If you want to refine them later, you can refer to the examples shown in the sections below.
|
||
:::
|
||
|
||
```bash
|
||
npx @mui/codemod@latest v5.0.0/jss-to-styled <path>
|
||
```
|
||
|
||
Example transformation:
|
||
|
||
```diff
|
||
import Typography from '@mui/material/Typography';
|
||
-import makeStyles from '@mui/styles/makeStyles';
|
||
+import { styled } from '@mui/material/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>
|
||
)
|
||
}
|
||
```
|
||
|
||
:::success
|
||
You should run this codemod on a small chunk of files and then check the changes before continuing, because in some cases you might need to adjust the code after the transformation—this codemod won't cover all cases.
|
||
:::
|
||
|
||
#### Manual
|
||
|
||
We recommend `sx` API over `styled` for creating responsive styles or overriding minor CSS.
|
||
[Read more about `sx` here](/system/getting-started/the-sx-prop/).
|
||
|
||
```diff
|
||
import Chip from '@mui/material/Chip';
|
||
-import makeStyles from '@mui/styles/makeStyles';
|
||
+import Box from '@mui/material/Box';
|
||
|
||
-const useStyles = makeStyles((theme) => ({
|
||
- wrapper: {
|
||
- display: 'flex',
|
||
- },
|
||
- chip: {
|
||
- padding: theme.spacing(1, 1.5),
|
||
- boxShadow: theme.shadows[1],
|
||
- }
|
||
-}));
|
||
|
||
function App() {
|
||
- const classes = useStyles();
|
||
return (
|
||
- <div className={classes.wrapper}>
|
||
- <Chip className={classes.chip} label="Chip" />
|
||
- </div>
|
||
+ <Box sx={{ display: 'flex' }}>
|
||
+ <Chip label="Chip" sx={{ py: 1, px: 1.5, boxShadow: 1 }} />
|
||
+ </Box>
|
||
);
|
||
}
|
||
```
|
||
|
||
In some cases, you might want to create multiple styled components in a file instead of increasing CSS specificity.
|
||
|
||
For example:
|
||
|
||
```diff
|
||
-import makeStyles from '@mui/styles/makeStyles';
|
||
+import { styled } from '@mui/material/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 = styled('div')(({ theme }) => ({
|
||
+ display: 'flex',
|
||
+ alignItems: 'center',
|
||
+ borderRadius: 20,
|
||
+ background: theme.palette.grey[50],
|
||
+}))
|
||
|
||
+const Label = styled('span')(({ theme }) => ({
|
||
+ color: theme.palette.primary.main,
|
||
+}))
|
||
|
||
function Status({ label }) {
|
||
- const classes = useStyles();
|
||
return (
|
||
- <div className={classes.root}>
|
||
- {icon}
|
||
- <span className={classes.label}>{label}</span>
|
||
- </div>
|
||
+ <Root>
|
||
+ {icon}
|
||
+ <Label>{label}</Label>
|
||
+ </Root>
|
||
)
|
||
}
|
||
```
|
||
|
||
:::success
|
||
[This jss-to-styled tool](https://siriwatk.dev/tool/jss-to-styled) helps convert JSS to multiple styled components without increasing CSS specificity.
|
||
|
||
This tool is _not_ maintained by MUI.
|
||
:::
|
||
|
||
### 2. Use [tss-react](https://github.com/garronej/tss-react)
|
||
|
||
:::error
|
||
This API will not work if you are [using `styled-components` as the underlying styling engine in place of `@emotion`](/material-ui/integrations/interoperability/#styled-components).
|
||
:::
|
||
|
||
The API is similar to JSS `makeStyles`, but under the hood, it uses `@emotion/react`.
|
||
It also features much better TypeScript support than v4's `makeStyles`.
|
||
|
||
In order to use it, you'll need to add it to your project's dependencies:
|
||
|
||
With npm:
|
||
|
||
```bash
|
||
npm install tss-react
|
||
```
|
||
|
||
With yarn:
|
||
|
||
```bash
|
||
yarn add tss-react
|
||
```
|
||
|
||
#### Codemod
|
||
|
||
We provide [a codemod](https://github.com/mui/material-ui/blob/master/packages/mui-codemod/README.md#jss-to-tss-react) to help migrate JSS styles to the `tss-react` API.
|
||
|
||
```bash
|
||
npx @mui/codemod@latest v5.0.0/jss-to-tss-react <path>
|
||
```
|
||
|
||
Example transformation:
|
||
|
||
```diff
|
||
import * as React from 'react';
|
||
-import makeStyles from '@material-ui/styles/makeStyles';
|
||
+import { makeStyles } from 'tss-react/mui';
|
||
import Button from '@mui/material/Button';
|
||
import Link from '@mui/material/Link';
|
||
|
||
-const useStyles = makeStyles((theme) => {
|
||
+const useStyles = makeStyles()((theme) => {
|
||
return {
|
||
root: {
|
||
color: theme.palette.primary.main,
|
||
},
|
||
apply: {
|
||
marginRight: theme.spacing(2),
|
||
},
|
||
};
|
||
});
|
||
|
||
function Apply() {
|
||
- const classes = useStyles();
|
||
+ const { classes } = useStyles();
|
||
|
||
return (
|
||
<div className={classes.root}>
|
||
<Button component={Link} to="https://support.mui.com" className={classes.apply}>
|
||
Apply now
|
||
</Button>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default Apply;
|
||
```
|
||
|
||
If you were using the `$` syntax and `clsx` to combine multiple CSS classes,
|
||
the transformation would look like this:
|
||
|
||
```diff
|
||
import * as React from 'react';
|
||
-import { makeStyles } from '@material-ui/core/styles';
|
||
-import clsx from 'clsx';
|
||
+import { makeStyles } from 'tss-react/mui';
|
||
|
||
-const useStyles = makeStyles((theme) => ({
|
||
+const useStyles = makeStyles<void, 'child' | 'small'>()((theme, _params, classes) => ({
|
||
parent: {
|
||
padding: 30,
|
||
- '&:hover $child': {
|
||
+ [`&:hover .${classes.child}`]: {
|
||
backgroundColor: 'red',
|
||
},
|
||
},
|
||
small: {},
|
||
child: {
|
||
backgroundColor: 'blue',
|
||
height: 50,
|
||
- '&$small': {
|
||
+ [`&.${classes.small}`]: {
|
||
backgroundColor: 'lightblue',
|
||
height: 30
|
||
}
|
||
},
|
||
}));
|
||
|
||
function App() {
|
||
- const classes = useStyles();
|
||
+ const { classes, cx } = useStyles();
|
||
return (
|
||
<div className={classes.parent}>
|
||
<div className={classes.child}>
|
||
Background turns red when the mouse hovers over the parent.
|
||
</div>
|
||
- <div className={clsx(classes.child, classes.small)}>
|
||
+ <div className={cx(classes.child, classes.small)}>
|
||
Background turns red when the mouse hovers over the parent.
|
||
I am smaller than the other child.
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default App;
|
||
```
|
||
|
||
:::error
|
||
When using JavaScript (rather than TypeScript), remove `<void, 'child' | 'small'>`.
|
||
:::
|
||
|
||
The following is a comprehensive example using the `$` syntax, `useStyles()` parameters, merging in classes from a `classes` prop ([see doc](https://docs.tss-react.dev/your-own-classes-prop)) and [an explicit name for the stylesheet](https://docs.tss-react.dev/api/makestyles#naming-the-stylesheets-useful-for-debugging-and-theme-style-overrides).
|
||
|
||
```diff
|
||
-import clsx from 'clsx';
|
||
-import { makeStyles, createStyles } from '@material-ui/core/styles';
|
||
+import { makeStyles } from 'tss-react/mui';
|
||
|
||
-const useStyles = makeStyles((theme) => createStyles<
|
||
- 'root' | 'small' | 'child', {color: 'primary' | 'secondary', padding: number}
|
||
->
|
||
-({
|
||
- root: ({color, padding}) => ({
|
||
+const useStyles = makeStyles<{color: 'primary' | 'secondary', padding: number}, 'child' | 'small'>({name: 'App'})((theme, { color, padding }, classes) => ({
|
||
+ root: {
|
||
padding: padding,
|
||
- '&:hover $child': {
|
||
+ [`&:hover .${classes.child}`]: {
|
||
backgroundColor: theme.palette[color].main,
|
||
}
|
||
- }),
|
||
+ },
|
||
small: {},
|
||
child: {
|
||
border: '1px solid black',
|
||
height: 50,
|
||
- '&$small': {
|
||
+ [`&.${classes.small}`]: {
|
||
height: 30
|
||
}
|
||
}
|
||
-}), {name: 'App'});
|
||
+}));
|
||
|
||
function App({classes: classesProp}: {classes?: any}) {
|
||
- const classes = useStyles({color: 'primary', padding: 30, classes: classesProp});
|
||
+ const { classes, cx } = useStyles({
|
||
+ color: 'primary',
|
||
+ padding: 30
|
||
+ }, {
|
||
+ props: {
|
||
+ classes: classesProp
|
||
+ }
|
||
+ });
|
||
|
||
return (
|
||
<div className={classes.root}>
|
||
<div className={classes.child}>
|
||
The Background take the primary theme color when the mouse hovers the parent.
|
||
</div>
|
||
- <div className={clsx(classes.child, classes.small)}>
|
||
+ <div className={cx(classes.child, classes.small)}>
|
||
The Background take the primary theme color when the mouse hovers the parent.
|
||
I am smaller than the other child.
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default App;
|
||
```
|
||
|
||
After running the codemod, search your code for "TODO jss-to-tss-react codemod" to find cases that the codemod could not handle reliably.
|
||
|
||
There may be other cases beyond those with TODO comments that are not handled fully by the codemod—particularly if parts of the styles are returned by functions.
|
||
|
||
If the styles buried within a function use the `$` syntax or `useStyles` params, then those styles won't be migrated appropriately.
|
||
|
||
:::error
|
||
You should drop [`clsx`](https://www.npmjs.com/package/clsx) in favor of [`cx`](https://emotion.sh/docs/@emotion/css#cx).
|
||
|
||
The key advantage of `cx` is that it detects Emotion-generated class names to ensure that styles are overwritten in the correct order.
|
||
|
||
The default precedence of styles from multiple CSS classes is different between JSS and tss-react and some manual re-ordering of `cx` parameters
|
||
may be necessary—see [this issue comment](https://github.com/mui/material-ui/pull/31802#issuecomment-1093478971) for more details.
|
||
:::
|
||
|
||
To ensure that your class names always includes the actual name of your components, you can provide the `name` as an implicitly named key (`name: { App }`).
|
||
|
||
See [this tss-react doc](https://docs.tss-react.dev/api/makestyles#naming-the-stylesheets-useful-for-debugging-and-theme-style-overrides) for details.
|
||
|
||
You may end up with ESLint warnings [like this one](https://user-images.githubusercontent.com/6702424/148657837-eae48942-fb86-4516-abe4-5dc10f44f0be.png) if you deconstruct more than one item.
|
||
|
||
Don't hesitate to disable `eslint(prefer-const)`, [like this](https://github.com/thieryw/gitlanding/blob/b2b0c71d95cfd353979c86dfcfa1646ef1665043/.eslintrc.js#L17) in a regular project.
|
||
|
||
#### withStyles()
|
||
|
||
`tss-react` also features a [type-safe implementation](https://docs.tss-react.dev/api/withstyles) of [v4's `withStyles()`](https://v4.mui.com/styles/api/#withstyles-styles-options-higher-order-component).
|
||
|
||
:::info
|
||
The equivalent of the `$` syntax is also supported in tss's `withStyles()`.
|
||
[See doc](https://docs.tss-react.dev/nested-selectors#withstyles).
|
||
:::
|
||
|
||
```diff
|
||
-import Button from '@material-ui/core/Button';
|
||
+import Button from '@mui/material/Button';
|
||
-import withStyles from '@material-ui/styles/withStyles';
|
||
+import { withStyles } from 'tss-react/mui';
|
||
|
||
const MyCustomButton = withStyles(
|
||
+ Button,
|
||
(theme) => ({
|
||
root: {
|
||
minHeight: '30px',
|
||
},
|
||
textPrimary: {
|
||
color: theme.palette.text.primary,
|
||
},
|
||
'@media (min-width: 960px)': {
|
||
textPrimary: {
|
||
fontWeight: 'bold',
|
||
},
|
||
},
|
||
}),
|
||
-)(Button);
|
||
+);
|
||
|
||
export default MyCustomButton;
|
||
```
|
||
|
||
#### Theme style overrides
|
||
|
||
[Global theme overrides](https://v4.mui.com/customization/components/#global-theme-override) are supported out of the box by TSS.
|
||
|
||
Follow the instructions in the relevant section of the [Breaking changes](/material-ui/migration/v5-style-changes/#restructure-component-definitions) doc, and [provide a `name` to `makeStyles`](https://docs.tss-react.dev/api/makestyles#naming-the-stylesheets-useful-for-debugging-and-theme-style-overrides).
|
||
|
||
In Material UI v5, [style overrides also accept callbacks](https://mui.com/material-ui/customization/theme-components/).
|
||
|
||
By default, TSS is only able to provide the theme.
|
||
If you want to provide the props and the `ownerState`, [please refer to this documentation](https://docs.tss-react.dev/mui-global-styleoverrides).
|
||
|
||
:::warning
|
||
tss-react is _not_ maintained by MUI.
|
||
|
||
If you have any question about how to setup SSR (Next.js), or if you are wondering
|
||
how to customize the `theme` object, please refer to the [tss-react documentation](https://github.com/garronej/tss-react#mui-integration).
|
||
|
||
You can also [submit an issue](https://github.com/garronej/tss-react/issues/new) for any bug or feature request, and [start a discussion](https://github.com/garronej/tss-react/discussions) if you need help.
|
||
:::
|
||
|
||
## Complete the migration
|
||
|
||
Once you migrate all of the styling, remove unnecessary `@mui/styles` by uninstalling the package.
|
||
|
||
With npm:
|
||
|
||
```bash
|
||
npm uninstall @mui/styles
|
||
```
|
||
|
||
With yarn:
|
||
|
||
```bash
|
||
yarn remove @mui/styles
|
||
```
|
||
|
||
:::warning
|
||
`@emotion/styled` is a peer dependency of `@mui/material`.
|
||
You must keep it in your dependencies even if you never explicitly use it.
|
||
:::
|