Giới thiệu
MUI (Material UI) là một thư viện React phổ biến cho việc xây dựng giao diện người dùng phong phú với nhiều tính năng. Dù có nhiều đối thủ cạnh tranh như Ant Design hay Shadcn, MUI vẫn là lựa chọn ưu tiên của tôi khi cần tạo ra các ứng dụng frontend mạnh mẽ cho doanh nghiệp. Thư viện này cung cấp một bộ component phong phú, khả năng tùy biến linh hoạt và hỗ trợ truy cập mạnh mẽ ngay từ đầu.
Tuy nhiên, do sức mạnh của MUI, việc quản lý có thể trở nên rối rắm. Chắc chắn bạn đã từng trải qua sự bực bội này. Tôi đã sử dụng thư viện này từ phiên bản 4 cho nhiều dự án của mình và trong bài viết này, tôi sẽ chia sẻ ba mẹo nhanh mà tôi đã học được để bạn có thể tiết kiệm thời gian và phát triển các component dễ bảo trì và dễ kiểm thử hơn.
Quy tắc số một - Tùy biến từ theme toàn cục
Luôn bắt đầu khám phá khả năng tùy biến từ cấu hình theme toàn cục.
Cấu hình theme toàn cục tập trung các kiểu dáng cho tất cả các component MUI, đảm bảo sự đồng nhất trong toàn bộ ứng dụng. Khi bạn thực hiện điều chỉnh cho một component Material UI trong theme toàn cục, nó sẽ được áp dụng cho tất cả các thể hiện của component này trong ứng dụng.
Ví dụ: Giả sử bạn muốn thay đổi kiểu dáng của nút. Nút mặc định của MUI sử dụng phong cách Material Design 2:

Nhưng nếu bạn muốn nó trông giống như giao diện của GitHub? Để làm điều đó, bạn cần tạo một cấu hình theme mới và tùy biến component MuiButton
thông qua nó:
typescript
// theme.tsx
import { createTheme } from "@mui/material";
export const theme = createTheme({
components: {
MuiButton: {
styleOverrides: {
root: {
textTransform: "none",
boxShadow: "none",
padding: "0 12px",
height: 32,
borderRadius: 6,
backgroundColor: "rgb(45, 164, 78)",
border: "1px solid rgba(27, 31, 36, 0.15)",
},
},
},
},
});
Lưu ý: Luôn sử dụng các màu từ bảng màu. Đừng đưa chúng trực tiếp dưới dạng chuỗi vào cấu hình theme của bạn. Tôi đã làm điều này để tránh cấu hình phức tạp và tập trung vào quy tắc.
Đừng quên sử dụng ThemeProvider
để chèn theme tùy chỉnh vào ứng dụng của bạn:
typescript
// main.tsx
createRoot(document.getElementById("root")!).render(
<StrictMode>
<CssBaseline />
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
</StrictMode>
);
Nếu theme không cung cấp đủ sự linh hoạt, quy tắc thứ hai sẽ giúp bạn.
Quy tắc số hai - Tạo component styled
Nếu bạn đã điều tra kỹ lưỡng tất cả các design tokens và props mà theme cung cấp mà không tìm thấy prop bạn muốn tùy biến, đã đến lúc tạo một component styled mới.
Giả sử bạn muốn có hai kiểu dáng cho nút: round
và square
. Mặc định, MUI không cung cấp cho chúng ta thuộc tính shape
, nhưng chúng ta có thể dễ dàng thêm nó bằng cách tạo một component styled mới:
typescript
// button.styled.ts
import { Button as MuiButton, styled, type ButtonProps as MuiButtonProps } from "@mui/material";
const shouldForwardProp = (prop: string) => {
if (!prop || typeof prop !== "string") return false;
return !prop.trim().startsWith("$");
};
type ButtonProps = MuiButtonProps & {
$shape?: "round" | "square";
};
export const Button = styled(MuiButton, { shouldForwardProp })<ButtonProps>(({ $shape = "square" }) => {
const shapeStyle = {
round: { borderRadius: 100 },
square: {},
};
return shapeStyle[$shape];
});
Những điểm quan trọng từ đoạn mã trên:
- Luôn thêm ký hiệu
$
hoặc một ký hiệu hợp lệ khác vào đầu thuộc tính tùy chỉnh của bạn. Sau đó, sử dụng hàmshouldForwardProp
để ngăn thuộc tính tùy chỉnh được chuyển tiếp đến phần tử DOM bên dưới, ngăn MUI đưa ra thông báo lỗi “Invalid attribute name:shape
” trong bảng điều khiển. - Sử dụng hàm
styled
từ gói@mui/material
, không phải từ@emotion/styled
. Emotion có cú pháp khác và không nhận biết tất cả các component MUI và cấu trúc nội bộ mà Material UI thêm vào trên Emotion.
Đảm bảo rằng bạn nhập component nút styled của mình thay vì nút mặc định của MUI ở mọi nơi bạn sử dụng nó:
typescript
// App.tsx
import { Stack } from "@mui/material";
import { Button } from "./button.styled";
function App() {
return (
<Stack p={2} spacing={1} sx={{ width: 100 }}>
<Button variant="contained">Square</Button>
<Button variant="contained" $shape="round">Round</Button>
</Stack>
);
}
export default App;
Kết quả:
Nếu theme không đáp ứng được nhu cầu của bạn, bạn cũng có thể bị cám dỗ sử dụng prop sx
. Đây là lý do tại sao việc tạo component styled lại tốt hơn.
Tại sao không nên sử dụng sx
?
Prop sx
là tốt cho việc thêm 2-3 kiểu dáng inline nhanh chóng. Nhưng nếu bạn có logic kiểu dáng phức tạp hơn, hãy cân nhắc sử dụng các component styled như tôi đã chỉ ra ở trên. Với cách tiếp cận này, bạn sẽ tách biệt logic kiểu dáng và logic kinh doanh, từ đó cải thiện khả năng đọc và bảo trì.
Đôi khi, tùy biến vượt ra ngoài kiểu dáng. Trong trường hợp đó, quy tắc thứ ba sẽ được áp dụng.
Kiểu dáng không giải quyết được? Tạo component React
Giả sử chúng ta có yêu cầu thêm hiệu ứng âm thanh mỗi khi người dùng nhấp vào nút. Các component styled không thể đạt được điều này, và MUI cũng không cung cấp bất kỳ tokens thiết kế hoặc khả năng nào để thêm âm thanh.
Khi việc tùy biến liên quan đến logic phức tạp, chúng ta tạo một wrapper React mới quanh nút styled:
typescript
// Button.tsx
import useSound from "use-sound";
import buttonSound from "../public/button.mp3";
import type { FC } from "react";
import { type ButtonProps, Button as ButtonStyled } from "./button.styled";
export const Button: FC<ButtonProps> = (props) => {
const [play] = useSound(buttonSound);
return <ButtonStyled {...props} onClick={() => play()}></ButtonStyled>;
};
Bây giờ hãy sử dụng component nút mới trong ứng dụng của bạn:
typescript
// App.tsx
import { Stack } from "@mui/material";
import { Button } from "./Button";
function App() {
return (
<Stack p={2} spacing={1} sx={{ width: 100 }}>
<Button variant="contained">Square</Button>
<Button variant="contained" $shape="round">Round</Button>
</Stack>
);
}
export default App;
Kết luận
Chúc mừng bạn! 🎉
Bây giờ bạn đã biết cách giữ cho hệ thống thiết kế dựa trên MUI của bạn gọn gàng. Bạn đã học được:
- Tại sao và cách tùy biến các component thông qua theme toàn cục khi có thể.
- Trường hợp nào nên sử dụng component styled.
- Trường hợp nào bạn nên tạo một wrapper React riêng cho component MUI.
Nếu không có sự phân cấp này, bạn sẽ kết thúc việc trộn lẫn các override theme, inline sx
, và các hack wrapper ở những nơi ngẫu nhiên.
Bằng cách áp dụng ba quy tắc dễ làm theo này, bạn sẽ xây dựng các hệ thống thiết kế dễ bảo trì, kiểm thử và tài liệu. Các đồng nghiệp và chính bạn trong tương lai sẽ rất trân trọng điều này 🙂
Bạn muốn biết thêm? 🔥
Hãy kiểm tra bài viết trước của tôi “Kiến trúc React MVVM với TanStack Router: Giới thiệu” nơi tôi dạy về React MVVM với một công nghệ hiện đại.
Việc tạo nội dung như thế này không phải là một hành trình dễ dàng, vì vậy tôi hy vọng bạn sẽ nhấn nút thích và để lại bình luận. Như thường lệ, phản hồi xây dựng luôn được chào đón!
🔗 Theo dõi tôi trên LinkedIn
📰 Đọc tôi trên nền tảng bạn chọn: Substack, DEV.to
Hẹn gặp lại lần sau! 👋