MUI Sandbox @ github
MUI 架構
Understanding MUI packages @ MUI v5
MUI Core
- Material UI(
@mui/material):使用基於 Material Design 所設計出來的元件,同時包含「功能」和「樣式」。 - MUI Base(
@mui/base):包含許多 Unstyled(headless)的 React UI 元件,只是用提供的「功能」,CSS 樣式的部分需要自己處理。 - MUI System(
@mui/system):相對於只用「功能」的 MUI Base,MUI System 則是提供許多 CSS Utilities 可以使用,特別像是sx,它需要搭配 MUI Base、MUI components 使用。
MUI System
MUI System @ MUI 5
sx
The sx prop @ MUI 5
MUI system 的 sx 特別適合用在一次性(one-ff)的元件。
一般使用
到 Properties 可以找到所有可以用的屬性名稱,以及可以直接使用到的 theme mapping,並且看 ${value} 的地方。
舉例來說:

- 如果寫
{ border: 3 },表示的即是border: 3px solid - 如果寫
{ borderColor: 'success.dark' },對應的會是theme.palette.success.dark的顏色 - 如果寫
{ gap: 3 },對應到的是theme.spacing(3) - 如果寫
{ display: 'inline-block' },因為這裡沒有任何 mapping(none),所以就是直接帶入該值 - 如果寫
{ fontFamily: 'fontFamily' },因為它的 mapping 是theme.typography[value],因此會套用theme.typography.fontFamily,表示會套用預設的 font family。
一般來說,搭配 <Box /> 使用 sx 是最簡單的方式:
<Box
sx={{
border: 1, // 1px solid
bgcolor: 'background.paper', // bgcolor 的 "c" 是小寫,theme.palette.background.paper
boxShadow: 1, // theme.shadows[1]
borderRadius: 2, // theme.shape.borderRadius * 2
p: 2, // theme.spacing(2)
minWidth: 300, // 300px
fontFamily: 'fontFamily', // theme.typography.fontFamily
}}
/>
除了可以把上述的 System keys 放在 sx 中使用外,也可以把它們當成 <Box /> 的 props 帶入:
// 個人認為還是把 CSS 相關的屬性放在 `sx` 中集中管理比較好,和「功能」較相關的再用 props 傳下去。
<Box color="text.primary" fontSize={32} fontWeight="medium" fontFamily="fontFamily">
98.3 K
</Box>
個人認為還是把 CSS 相關的屬性放在 sx 中集中管理比較好,和「功能」較相關的再用 props 傳下去。
callback value:需要使用到 theme 時
需要的話,sx 的「值」或「屬性值」都可以帶 callback function,特別適合用在需要使用 theme 的時候:
// 屬性值使用 callback
<Box
sx={{
height: (theme) => theme.spacing(2),
}}
/>
// sx 的值使用 callback
<Box
sx={(theme) => ({
...theme.typography.body1,
color: theme.palette.primary.main
})}
/>
Array values:特定情況下才套用特定樣式
<Box
sx={[
{
'&:hover': {
bgcolor: 'info.main',
color: 'info.contrastText',
},
},
checked && {
'&:hover': {
bgcolor: 'success.main',
color: 'success.contrastText',
},
},
]}
>
Array Values
</Box>
把 sx 當成 props 傳遞
Passing the sx prop @ MUI 5
- 定義型別的時候使用
SxProps<Theme>
// credit: https://mui.com/system/getting-started/the-sx-prop/#passing-the-sx-prop
import { SxProps, Theme } from '@mui/material/styles';
interface ListHeaderProps {
children: React.ReactNode;
sx?: SxProps<Theme>;
}
function ListHeader({ sx = [], children }: ListHeaderProps) {
return (
<ListItem
sx={[
{
width: 'auto',
textDecoration: 'underline',
},
// You cannot spread `sx` directly because `SxProps` (typeof sx) can be an array.
...(Array.isArray(sx) ? sx : [sx]),
]}
>
<FormLabel sx={{ color: 'inherit' }}>{children}</FormLabel>
</ListItem>
);
}
搭配 TypeScript 使用
如果你想要把套用到 sx 的物件存成一個變數使用,你「可能」需要搭配 as const:
const style = {
bgcolor: 'background.paper', // bgcolor 的 "c" 是小寫
boxShadow: 1,
borderRadius: 2, // theme.shape.borderRadius * 1
p: 2,
minWidth: 300,
flexDirection: 'column',
} as const;
<Box sx={style} />;
筆者實測沒有使用 as const 也沒有發生型別上的錯誤(2022.10.30)。
客製化(Customization)
MUI Playground @ code sandbox
Theme
createMuiTheme 是 material ui 4 的用法;createTheme 是 MUI 5 的用法。
查看 Default theme 可以知道所有可以覆蓋的預設樣式。
useTheme
在元件中,可以使用 useTheme 來取得 theme,或是用 theme 中提供的 utility,例如 theme.spacing()。
修改預設主題樣式
Theming @ MUI > Customization
使用 createTheme() 可以添加或修改預設的樣式,它會回傳一個新的 theme,接著只要把這個 theme 放到 <ThemeProvider> 中,即可套用新的主題樣式。
在 MUI 中,所有預設的樣式都列在 Default theme 中,可以找到欲改變的屬性並覆蓋即可。
舉例來說,如果想改變預設 primary 的樣式,在文件 Default theme 中可以看到它的屬性被放在 palette.primary 中:

接著使用 createTheme 將該屬性的預設值覆蓋掉,並帶入 <ThemeProvider> 即可覆蓋掉預設的樣式:
import { orange } from '@mui/material/colors';
import { createTheme } from '@mui/material/styles';
export const theme = createTheme({
// 修改預設的樣式
palette: {
primary: {
main: orange[300],
// light: MUI 會自動根據 main 來推算顏色
dark: orange[500],
// contrastText: MUI 會自動根據 main 來推算顏色
},
},
});
export default function CustomStyles() {
return (
<ThemeProvider theme={theme}>
<CustomCheckbox defaultChecked />
</ThemeProvider>
);
在主題樣式中添加新的屬性(Custom variables)
透過 createTheme 也可以在 theme 中添加新的屬性。
舉例來說,如果想要在 theme 中添加一個 status.danger 的屬性,可以直接把該屬性放入 theme 的物件中:
export const theme = createTheme({
// 添加新的屬性與樣式
status: {
danger: red[600],
},
});
但如果需搭配 TypeScript 使用的話,由於預設在 theme 中沒有這個屬性,因此型別檢查會有問題,這時候可以使用 TypeScript 中的 Module Augmentation 來擴增 Theme 的型別定義:
// 透過 Module Augmentation 來擴增 Theme 的型別定義
// 這裡會在 Theme 中增添 `status.danger`
declare module '@mui/material/styles' {
// 能夠在 theme 中找到這個屬性
interface Theme {
status: {
danger: React.CSSProperties['color']; // 或用 string
};
}
// 能夠透過 createTheme 新增屬性
interface ThemeOptions {
status: {
danger: React.CSSProperties['color']; // 或用 string
};
}
}
如此,就可以在 theme 中取得自訂義的樣式:
import { Checkbox, styled } from '@mui/material';
import { useTheme } from '@mui/material/styles';
const CustomCheckbox = styled(Checkbox)(({ theme }) => ({
color: theme.status.danger,
}));
function App() {
const theme = useTheme();
return (
<div className="App">
<CustomCheckbox />
<span>{theme.status.danger}</span>
</div>
);
}
如果 使用 VSCode 的 auto import 要特別留意!styled 是從 @mui/material import 的,而不是從 '@emotion/styled' import 的。
Palette
在調色盤中的屬性可以在使用元件時,透過 color 來使用,包括新添加的屬性也可以。
例如:
<Button color="neutral" variant="contained">
修改調色盤中預設的色彩
Palette @ mui
在 MUI 中預設的調色盤(palette)包含:
- primary
- secondary
- error
- warning
- info
- success
每個調色盤中又包含:
- light
- main
- dark
- contrastText
透過前面提到的「修改預設主題樣式」可以修改它們的預設值,很酷的是「對於我們沒指定的屬性,MUI 會自動根據 main 來計算它們的顏色」,例如,如果我們只提供 palette.xxx.main 的話,MUI 會自動推算出 light、dark 和 contrastText 的顏色(計算的邏輯可以參考:Providing the colors directly)。
在調色盤中添加新的配色
Adding new colors @ MUI > Customization > Palette
一樣可以直接在 createTheme 的時候,將常用的配色添加到調色盤中:
export const theme = createTheme({
palette: {
// 添加新的配色
neutral: {
main: grey[500],
},
},
});
搭配 TypeScript 使用時,和修改 theme 一樣,可以透過 module augmentation 來擴增 TypeScript 的型別定義,主要是 Palette 和 PaletteOptions 避免 TS 找不到該屬性而報錯。
例如,這裡我們希望在添加 neutral 這個屬性在調色盤中:
import { grey } from '@mui/material/colors';
import { createTheme } from '@mui/material/styles';
declare module '@mui/material/styles' {
// 能夠在 palette 中找到這個屬性
interface Palette {
neutral: Palette['primary'];
}
// 能用使用 createTheme 來設定這個屬性
interface PaletteOptions {
neutral?: PaletteOptions['primary'];
}
}
export const theme = createTheme({
// 在調色盤中添加新的屬性
palette: {
neutral: {
main: grey[500],
// MUI 會根據 main 自動計算出 dark, light, contrastText 的顏色
},
},
});
另外由於使用 palette 的元件並不知道 color 中又新的屬性可以使用(雖然是可以直接使用的),因此,同樣要擴增該元件的型別定義。
例如,雖然可以直接用 <Button color="neutral">,但因為 Button 元件並不知道 color 有 neutral 可以用,所以 TS 會報錯,這時候可以擴增 ButtonPropsColorOverrides 的型別:
import Button from '@mui/material/Button';
declare module '@mui/material/Button' {
interface ButtonPropsColorOverrides {
neutral: true;
}
}
export default function CustomButton() {
return (
<Button color="neutral" variant="contained">
Neutral
</Button>
);
}
Typography
Typography @ MUI
在 MUI 中,typography 共有 13 種不同的 variants,其中每個 variant 中可以調整的屬性可參考 Default theme:
- h1 ~ h6
- subtitle1, subtitle2
- body1, body2
- button:修改所有 Button 的文字樣式。
- caption
- overline
針對 TypeScript,如果希望增加 Typography 中的屬性,可以使用 module augmentation 來擴增 TypographyVariants 和 TypographyVariantsOptions 的型別;並且針對使用此 variant 的元件,使用 TypographyPropsVariantOverrides 來擴增。詳細作法參考官網的 Adding & disabling variants。
Spacing
Spacing @ MUI