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の定義が優先されます
お気をつけを
おわり
備忘録代わりに殴り書きしました
そのままコピペしても使えない部分が結構ありそうですがご容赦