Upgrade material ui 4 makeStyles and withStyles to material ui 5 using emotion JS with TS
Tags: mui5, react, emotion, typescript
Published: 2022-06-17 11:04:42

I just came across a very nice blog when the upgrading to mui5 using emotion, it is nicely demonstrated [here](https://dev.to/atonchev/material-ui-5-the-easiest-way-to-migrate-from-makestyles-to-emotion-1i9l)
But there are a few things which are lacking in this implementation, i.e. TS support, how to handle withStyles Styled components.
In this blog post I would mention those missing items.

**_Styles Root part is same as mentioned in the above mentioned blog._**



## The emotion theme declaration

```
import { Theme as MuiTheme } from '@mui/material/styles'
import '@emotion/react'

declare module '@emotion/react' {
  // eslint-disable-next-line @typescript-eslint/no-empty-interface
  export interface Theme extends MuiTheme {}
}
```

## Custom hook with TS support

```
import { useMemo } from 'react'
import { css, CSSInterpolation } from '@emotion/css'
import { useTheme } from '@emotion/react'
import { Theme as MuiTheme } from '@mui/material/styles'

function useEmotionStyles(
  styles: () => Record<string, CSSInterpolation>
): Record<string, ReturnType<typeof css>>

function useEmotionStyles(
  styles: (theme: MuiTheme) => Record<string, CSSInterpolation>
): Record<string, ReturnType<typeof css>>

function useEmotionStyles<T>(
  styles: (theme: MuiTheme, props: T) => Record<string, CSSInterpolation>,
  props: T
): Record<string, ReturnType<typeof css>>

function useEmotionStyles<T>(
  styles: (theme: MuiTheme, props?: T) => Record<string, CSSInterpolation>,
  props?: T
): Record<string, ReturnType<typeof css>> {
  const theme = useTheme()
  return useMemo(() => {
    const classes = styles(theme, props)
    const classNameMap = {}

    Object.entries(classes).forEach(([key, value]) => {
      classNameMap[key] = css(value)
    })

    return classNameMap
  }, [props, styles, theme])
}

export default useEmotionStyles
```

Here we have a overloaded hook for possible calls to the hook.

A simple example would be 

```
type GridProps = { itemMargin: number | string }

const gridStyles = (theme: Theme, { itemMargin }: GridProps) => ({
  container: {
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
    flexWrap: 'wrap' as CSSTypes.Property.FlexWrap,
    maxWidth: theme.breakpoints.values.md,
    [theme.breakpoints.down('sm')]: {
      maxWidth: 420
    },
    '&>*': {
      margin: itemMargin
    }
  }
})


const Component = () => {
  const { container } = useEmotionStyles<GridProps>(gridStyles, { itemMargin })

  return (
    <Container className={container}>
  )
}
```

If you want to implement keyframes animations using emotion you can use this way.

```
import { css, keyframes } from '@emotion/react'

const fadeIn = keyframes({
  '0%': {
    opacity: 0
  },
  '100%': {
    opacity: 1
  }
})
const styles = () => ({
  text: css({
    display: 'flex',
    alignItems: 'center',
    animation: `${fadeIn} 2s`
  })
})

```


## Custom Hook for Styled Component (replacement for withStyles)

```
import React, { useMemo } from 'react'
import { Theme, useTheme } from '@emotion/react'
import { Theme as MuiTheme } from '@mui/material/styles'
import styled, { StyledComponent } from '@emotion/styled/macro'
import { CSSInterpolation } from '@emotion/css'
import {
  OverridableComponent,
  OverridableTypeMap
} from '@mui/material/OverridableComponent'

type ReturnedType<T extends ComponentType> = StyledComponent<
  JSX.LibraryManagedAttributes<T, React.ComponentProps<T>> & {
    theme?: Theme
  }
>

type ComponentType =
  | OverridableComponent<OverridableTypeMap>
  | React.JSXElementConstructor<JSX.Element>
  | ((props?: React.ComponentProps<any>) => JSX.Element)

function useEmotionStyledComponent<T extends ComponentType>(
  styles: () => Record<string, CSSInterpolation>,
  WrappedComponent: T
): ReturnedType<T>

function useEmotionStyledComponent<T extends ComponentType>(
  styles: (theme: MuiTheme) => Record<string, CSSInterpolation>,
  WrappedComponent: T
): ReturnedType<T>

function useEmotionStyledComponent<T extends ComponentType, R>(
  styles: (theme: MuiTheme, props: R) => Record<string, CSSInterpolation>,
  WrappedComponent: T,
  props: R
): ReturnedType<T>

function useEmotionStyledComponent<T extends ComponentType, R>(
  styles: (theme: MuiTheme, props?: R) => Record<string, CSSInterpolation>,
  WrappedComponent: T,
  props?: R
): ReturnedType<T> {
  const theme = useTheme()
  return useMemo(() => {
    const strings = styles(theme, props)

    return styled(WrappedComponent)(strings?.root)
  }, [WrappedComponent, props, styles, theme])
}

export default useEmotionStyledComponent

```

To use this hook there must be only one root element in styles and all styles must be inside it.

```
const StyledDialog = (props: DialogProps) => {
  const Component = useEmotionStyledComponent<typeof Dialog>(
    (theme: Theme) => ({
      root: {
        '& div.MuiDialog-container': {
          height: 'auto'
        },
        '& div.MuiDialog-paper': {
          alignItems: 'center',
          padding: theme.spacing(0, 2, 2, 2),
          minWidth: 240
        }
      }
    }),
    Dialog
  )
  return <Component {...props} />
}

const MenuButton = (props: FabProps) => {
  const StyledMenuButton = useEmotionStyledComponent<typeof Fab, FabProps>(
    (theme: Theme) => ({
      root: {
        position: 'fixed',
        top: theme.spacing(2),
        left: theme.spacing(4)
      }
    }),
    Fab,
    props
  )
  return <StyledMenuButton {...props} />
}
```

and use this component as a Styled component.


With this two custom hooks you can replace the makeStyles and withStyles, if you have any questions, let me know.