MUI(Material UI v5)でEmotionを利用する
React + TypeScriptの環境でMUIとEmotionを併用する際についてのメモです
MUIとEmotionのインストール
Reactの環境構築は割愛します
MUIとEmotionをまとめてインストール
$ npm install @mui/material @emotion/react @emotion/styled
or
$ yarn add @mui/material @emotion/react @emotion/styled
その他必要なパッケージがあれば適宜
Emotionのcss propsを利用する
/* @jsxImportSource @emotion/react */ import React from 'react"; import { css } from '@emotion/react'; import Button from '@mui/material/Button'; const style = css({ background: 'red' }); const App = (): JSX.Element => { return ( <Button variant="contained" css={style}> ボタン </Button> ); }; export default App;
こんな感じ
おまじないを消す
Emotionをtsx(jsx)で利用する際はimport時に
/** @jsx jsx */
or /* @jsxImportSource @emotion/react */
といったおまじないを記述する必要があります。やってられません
なんでおまじないがいるの?って話はググってください
こんなことはやってられないので公式の解決策を利用します
$ npm add @emotion/babel-preset-css-prop
or
$ yarn add @emotion/babel-preset-css-prop
追加後、 .babelrc
かwebpackの babel-loader
の presets
に @emotion/babel-preset-css-prop
を追加します
{ test: /\.(js|mjs|jsx|ts|tsx)$/, loader: require.resolve('babel-loader'), options: { ..., presets: [ ..., '@emotion/babel-preset-css-prop', ],
こんな感じ。これにて解決
MUIのThemeをEmotionで利用する
MaterialUIのThemeをそのままEmotionで利用することができます
import { Box, Button } from '@mui/material'; import { createTheme } from '@mui/material'; import { Theme, ThemeProvider } from '@emotion/react'; const theme = createTheme(); const style = (theme: Theme) => css({ background: theme.palette.primary.main, }); const App = (): JSX.Element => { return ( <ThemeProvider theme={theme}> <Box> <Button variant="outlined" css={style}>hoge</Button> </Box> </ThemeProvider> ); } export default App;
ただしこれではMUI由来のThemeの項目を利用しようとすると型エラーが出ていしまいます
そこでEmotionのThemeをMUIのThemeの型を継承する形で上書きします
import { Theme as MUITheme } from '@mui/material/styles'; import theme from '@/Themes/Theme'; declare module '@emotion/react' { export interface Theme extends MUITheme {} }
こんな感じ
これで型エラーもなくpaletteやtypographyを利用できます
拡張
themeに項目を追加したいケースもあります
const theme = createTheme(theme, { width: { small: '5rem', medium: '10rem', large: '20rem', full: '100%' } });
その場合は先程の上書きした型に項目を追加してください
declare module '@emotion/react' { export interface Theme extends MUITheme { width: { small: string; medium: string; large: string; full: string; } } }
こんな感じ
styledの利用
styledもMUIと同様にThemeを用いながら使用できます
import React from 'react'; import { Box, Button } from '@mui/material'; import { styled as muiStyled } from '@mui/material/styles'; import styled from '@emotion/styled'; const StyledBox = styled(Box)(({ theme: Theme }) => ({ background: theme.palette.primary.main, })); // propsの型を定義して利用することも可能 const PropsStyledBox = styled(Box)<{ color: string }>(props => ({ background: props.theme.palette.primary.main, color: props.color, })); const MUIStyledBox = muiStyled(Box)(({ theme: Theme }) => ({ color: background: theme.palette.primary.dark, })); const Test = (): JSX.Element => { return ( <Box> <Box> normal box </Box> <StyledBox> emotion styled box </StyledBox> <PropsStyledBox color="red"}> emotion props styled box </StyledBox> <MUIStyledBox> mui styled box </MUIStyledBox> </Box> ); } export default Test;
注意点
sxで上書きする際の挙動がMUIとEmotionで異なります
import React from 'react'; import { Box, Button } from '@mui/material'; import { styled as muiStyled } from '@mui/material/styles'; import styled from '@emotion/styled'; const StyledBox = styled(Box)(({ theme: Theme }) => ({ background: theme.palette.primary.main, display: 'flex', justifyContent: 'center' })); const MUIStyledBox = muiStyled(Box)(({ theme: Theme }) => ({ background: theme.palette.primary.dark, display: 'flex', justifyContent: 'center' })); const Test = (): JSX.Element => { return ( <Box> <Box> normal box </Box> <StyledBox sx={{display: "flex", justifyContent: "flex-start"}}> emotion styled boxは justifyContent: centerになる </StyledBox> <MUIStyledBox sx={{display: "flex", justifyContent: "flex-start"}}> mui styled boxはjustifyContent: flex-startになる </MUIStyledBox> </Box> ); } export default Test;
MUIはsxでの定義が優先されますがEmotionはstyledの定義が優先されます
お気をつけを
おわり
備忘録代わりに殴り書きしました
そのままコピペしても使えない部分が結構ありそうですがご容赦