DEV Community

a-tonchev
a-tonchev

Posted on

Material UI 5 - the easiest way to migrate from makeStyles to emotion

Material UI version 5 has cool new stuff, and also many breaking changes. The migration tool is also amazing, but the style migration might not be that easy. Migrating a huge project could be a real pain.

Fortunately, there is a way to make a new, easy way to simply migrate the existing structure to emotion. Lets start:

Styles Root

At first, don't forget to setup correctly the root styles, emotion's ThemeProvider should override the default Material designs:

import React from 'react';
import { ThemeProvider as MuiThemeProvider, StylesProvider } from '@material-ui/core/styles';
import { ThemeProvider } from '@emotion/react';

const theme = {
  background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
};

const App = () => (
  <StylesProvider injectFirst>
    <MuiThemeProvider theme={theme}>
      <ThemeProvider theme={theme}>
        // All my components
      </ThemeProvider>
    </MuiThemeProvider>
  </StylesProvider>
);

export default App;
Enter fullscreen mode Exit fullscreen mode

The custom hook

For the custom hook, we will make use of the framework agnostic @emotion/css library, that can generate and inject classes from objects. More here

Then, lets make a custom hook, that can rebuild our makeStyles:

import { useMemo } from 'react';
import { css } from '@emotion/css';
import { useTheme } from '@emotion/react';

const useClasses = stylesElement => {
  const theme = useTheme();
  return useMemo(() => {
    const rawClasses = typeof stylesElement === 'function'
      ? stylesElement(theme)
      : stylesElement;
    const prepared = {};

    Object.entries(rawClasses).forEach(([key, value = {}]) => {
      prepared[key] = css(value);
    });

    return prepared;
  }, [stylesElement, theme]);
};

export default useClasses;

Enter fullscreen mode Exit fullscreen mode

This component will receive a object or a function with the styles, and create corresponding classes.

Then the last Step is - rework:

The old components

// TODO: Unwrap the function from makeStyles and rename useStyles
const useStyles = makeStyles(theme => ({
  paper: {
    marginTop: theme.spacing(8),
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
  },
  ...
}));


const TheComponent = () => {
 const classes = useStyles(); // useStyles from Material UI
}

Enter fullscreen mode Exit fullscreen mode

will become

// TODO: Unwrap the object from makeStyles and rename useStyles
const styles = theme => ({
  paper: {
    marginTop: theme.spacing(8),
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
  },
  ...
});

const TheComponent = () => {
 const classes = useClasses(styles); // useStyles from custom hook
}

Enter fullscreen mode Exit fullscreen mode

You can try also with pure object of classes:

The old component

// TODO: Unwrap the object from makeStyles and rename useStyles
const useStyles = makeStyles({
  paper: {
    marginTop: theme.spacing(8),
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
  },
  ...
});

const TheComponent = () => {
 const classes = useStyles(); // useStyles from Material UI
}
Enter fullscreen mode Exit fullscreen mode

will become:

const styles = {
  paper: {
    marginTop: theme.spacing(8),
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
  },
  //...
};

const TheComponent = () => {
 const classes = useClasses(styles); // useStyles from custom hook
}
Enter fullscreen mode Exit fullscreen mode

In summary:

  1. We setup the ThemeProvider and StylesProvider in our root component
  2. We create a custom hook useStyles
  3. We get rid of the makeStyles and unwrap the styles from it
  4. Rename useStyles e.g. to styles, because it is no more a hook
  5. Replace useStyles call inside of the component with our useClasses hook, while we put the styles object/function as argument

And with just a little bit rework, we already use emotion :)

With this approach, we managed to migrate a 2-years Project in 1 hour.

Best Regards
Anton Tonchev
JUST-SELL.online

Discussion (2)

Collapse
zionx4ever profile image
Reinaldo Zambrano

I have several cuestions about this, i implemented it on a tab component, and worked very well, then tried to use it on a ListItem and did nothing, neither on a Accordion.

Collapse
eadeveloper profile image
Edward Almanzar

What is the benefits of migrating to V5 from V4?