Nếu bạn đã từng làm việc với React, hẳn sẽ có lúc bạn tự hỏi: "Làm sao để giao diện của mình trông chuyên nghiệp và đẹp hơn, nhưng lại không muốn tốn hàng tá thời gian tự viết CSS từ đầu?" Hay bạn muốn ứng dụng của mình đồng nhất, dễ bảo trì, đồng thời có thể chỉnh màu, chỉnh font linh hoạt khi phát triển dự án?
MUI sẽ giúp bạn làm điều đó, bạn có thể nhanh chóng dựng giao diện từ những components có sẵn như: Button, Table, Form, Navigation… mà không cần mất công xây dựng lại tất cả từ số không. Hơn thế, MUI còn hỗ trợ theme cho toàn bộ ứng dụng, nên khi cần đổi từ giao diện sáng sang tối, hay thay đổi màu thương hiệu, bạn chỉ cần chỉnh một vài dòng code trong theme.
Bài này, mình sẽ cố gắng giải thích một cách dễ hiểu, kèm theo ví dụ minh họa rõ ràng, để dù bạn là người mới tiếp cận MUI hay đã có chút kinh nghiệm với React, vẫn có thể áp dụng hiệu quả.
1. MUI (Material UI) là gì?
Material UI là một thư viện UI mã nguồn mở bao gồm các React component, được tích hợp thêm cả Google's Material Design.
Trong MUI, có nhiều cách để có thể tuỳ biến style:
- sx prop: nhanh, linh hoạt, phù hợp khi custom cục bộ cho component.
- styled(): tạo ra component mới có style riêng biệt, phù hợp khi bạn muốn tái sử dụng nó ở nhiều nơi.
- Theme overrides: áp dụng style toàn cục, thay đổi mặc định cho tất cả component.
Mỗi cách đều có ưu điểm riêng, tuỳ thuộc vào trường hợp bạn sử dụng:
- sx prop: dùng khi bạn muốn điều chỉnh component ngay lập tức, ví dụ thêm margin, padding nhỏ.
- styled(): khi bạn cần tách logic style ra riêng, muốn tái sử dụng hoặc cần một component được "cá nhân hoá" từ component gốc.
- Theme overrides: khi bạn muốn thay đổi style mặc định ở tầm rộng hơn, ví dụ đổi màu nền mặc định cho tất cả các Button primary.
2. Tại sao nên sử dụng MUI?
- Tiết kiệm thời gian: bạn không cần phải tự xây dựng những component giao diện cơ bản từ đầu. Thư viện MUI đã làm điều đó cho bạn, giúp bạn chỉ cần tập trung vào nghiệp vụ, logic ứng dụng.
- Giao diện được thống nhất: sử dụng MUI giúp giao diện của bạn tuân thủ theo phong cách đồng nhất, không bị lệch design giữa các trang trong ứng dụng.
- Dễ tùy biến: chỉ cần chỉnh lại màu sắc, kích thước chữ, hay muốn giao diện tối (dark mode). MUI cũng sẽ hỗ trợ toàn diện theme.
- Cộng đồng lớn, tài liệu chi tiết: có rất nhiều ví dụ, hướng dẫn, và câu trả lời sẵn có, giúp bạn nhanh chóng khắc phục những khó khăn trong quá trình phát triển.
3. Sử dụng theme trong MUI
Đầu tiên, bạn muốn sử dụng MUI hãy chạy câu lệnh này để cài đặt trước đã nhé
pnpm install @mui/material @emotion/react @emotion/styled
MUI cung cấp hàm createTheme()
để tạo ra một theme. Sau đó bạn dùng ThemeProvider
để cung cấp theme cho toàn bộ ứng dụng.
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { createTheme, ThemeProvider } from '@mui/material/styles';
// ---------------------------------------------------------------
const theme = createTheme({
palette: {
primary: {
main: '#3f51b5',
},
secondary: {
main: '#f50057',
},
},
typography: {
fontFamily: '"Roboto","Helvetica","Arial",sans-serif',
},
});
ReactDOM.render(
<React.StrictMode>
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
</React.StrictMode>,
document.getElementById('root')
);
Ở đây, mình tạo một theme với palette và typography. ThemeProvider
bọc App
để toàn bộ ứng dụng nhận được theme này. Từ giờ trở đi, khi bạn sử dụng color="primary"
ở bất kỳ component nào (ví dụ Button), nó sẽ lấy màu từ theme.palette.primary.main
.
Bạn có thể mở rộng theme hơn nữa như thay đổi breakpoints, spacing, shape, zIndex, transitions,…
Dark Mode hiện đang rất phổ biến. Bạn có thể dễ dàng tạo theme dark bằng cách đặt mode: 'dark'
trong createTheme
.
const darkTheme = createTheme({
palette: {
mode: 'dark',
primary: {
main: '#90caf9'
},
secondary: {
main: '#f48fb1'
}
}
});
Sau đó sử dụng ThemeProvider
để áp dụng darkTheme. Bạn thậm chí có thể chuyển đổi giữa light/dark mode bằng cách lưu trạng thái và chọn theme tương ứng.
import { useState } from 'react';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import { Button } from '@mui/material';
// -----------------------------------------------------------
function DarkModeToggle() {
const [darkMode, setDarkMode] = useState(false);
const theme = createTheme({
palette: {
mode: darkMode ? 'dark' : 'light'
}
});
return (
<ThemeProvider theme={theme}>
<Button onClick={() => setDarkMode(!darkMode)}>
Kênh {darkMode ? 'Light' : 'Dark'} Mode
</Button>
<p>Đây là một đoạn văn để check màu nền.</p>
</ThemeProvider>
);
}
4. Sử dụng sx prop và styled để custom giao diện
Ngoài theme ra thì MUI v5 còn cung cấp sx prop, cho phép bạn viết style ngay trong JSX. sx sử dụng cùng ngôn ngữ theme, giúp bạn viết CSS dưới dạng đối tượng JS, có thể tương tác với theme.
<Button
variant="contained"
sx={{
backgroundColor: 'primary.main',
margin: 2,
'&:hover': {
backgroundColor: 'secondary.main',
}
}}
>
Sx Button
</Button>
Ở đây, sx cho phép bạn tham chiếu trực tiếp đến theme.palette.primary.main
. Bạn cũng có thể sử dụng CSS pseudo-classes (&:hover
) và các breakpoint có sẵn.
Nếu bạn cần tái sử dụng nhiều style, bạn có thể dùng styled() - một hàm tạo ra component với style tuỳ chỉnh.
import { styled } from '@mui/material/styles';
import Button from '@mui/material/Button';
// ---------------------------------------------
const MyButton = styled(Button)(({ theme }) => ({
backgroundColor: theme.palette.primary.main,
color: '#fff',
margin: theme.spacing(2),
'&:hover': {
backgroundColor: theme.palette.secondary.main,
},
}));
function App() {
return <MyButton>Styled Button</MyButton>;
}
Bạn có thể thấy trong đoạn code trên, MyButton là một component mới, sử dụng Button của MUI làm nền tảng, và áp dụng style custom.
5. Components thường dùng trong MUI
3.1 Typography
Typography dùng để hiển thị văn bản với các biến thể (variant) như: h1, h2, h3, body1, body2,...
import Typography from '@mui/material/Typography';
// -------------------------------------------------
function App() {
return (
<div>
<Typography variant="h1" component="h2" gutterBottom>
Tiêu đề chính
</Typography>
<Typography variant="body1">
Đây là một đoạn văn bản ví dụ. Bạn có thể tuỳ chỉnh font-size, màu,...
</Typography>
</div>
);
}
3.2 Button
Button có nhiều biến thể: text, outlined, contained và nhiều thuộc tính khác.
<Button variant="contained" color="primary">
Button
</Button>
<Button variant="outlined" color="secondary">
Button2
</Button>
<Button variant="text" disabled>
Disabled Button
</Button>
3.3 Container, Box và Grid
- Container dùng để bọc nội dung, giữ toàn bộ page trong cùng một chiều rộng xác định (thường responsive).
- Box là một component đa năng, bạn có thể coi như nó là một div nâng cao, hỗ trợ sx prop.
- Grid là hệ thống lưới, giúp sắp xếp layout responsive.
import { Container, Box, Grid, Paper } from '@mui/material';
// ---------------------------------------------------------
function LayoutExample() {
return (
<Container maxWidth="md">
<Box sx={{ my: 4 }}>
<Grid container spacing={2}>
<Grid item xs={12} sm={6}>
<Paper elevation={2} sx={{ p: 2 }}>
200Lab 1
</Paper>
</Grid>
<Grid item xs={12} sm={6}>
<Paper elevation={2} sx={{ p: 2 }}>
200Lab 2
</Paper>
</Grid>
</Grid>
</Box>
</Container>
);
}
export default LayoutExample;
3.4 Card
Card là component giúp bạn trình bày nội dung trong dạng thẻ. Thường đi kèm CardContent, CardMedia, CardActions.
import { Card, CardActions, CardContent, CardMedia, Button, Typography } from '@mui/material';
// ------------------------------------------------------
function CardExample() {
return (
<Card sx={{ maxWidth: 345 }}>
<CardMedia
component="img"
height="140"
image="https://placekitten.com/300/200"
alt="Cute Kitten"
/>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
Đây là title
</Typography>
<Typography variant="body2" color="text.secondary">
Đây là mô tả về gì đó
</Typography>
</CardContent>
<CardActions>
<Button size="small">Details</Button>
<Button size="small" color="secondary">Share</Button>
</CardActions>
</Card>
);
}
3.5 Dialog
Dialog giúp hiển thị nội dung trên màn hình, thường dùng cho xác nhận, thông báo hoặc cho form ngắn.
import { useState } from 'react';
import { Dialog, DialogTitle, DialogContent, DialogActions, Button, Typography } from '@mui/material';
// ---------------------------------------------------------
function DialogExample() {
const [open, setOpen] = useState(false);
return (
<>
<Button variant="outlined" onClick={() => setOpen(true)}>
Open Dialog
</Button>
<Dialog open={open} onClose={() => setOpen(false)}>
<DialogTitle>Confirm</DialogTitle>
<DialogContent>
<Typography>Bạn có chắc chắn muốn tiếp tục?</Typography>
</DialogContent>
<DialogActions>
<Button onClick={() => setOpen(false)}>Cancel</Button>
<Button onClick={() => { setOpen(false); alert('Đã xác nhận!'); }} color="primary" variant="contained">
Confirm
</Button>
</DialogActions>
</Dialog>
</>
);
}
3.6 Icon
MUI Icons cung cấp hàng nghìn biểu tượng Material Design. Bạn chỉ cần import biểu tượng và sử dụng như thế này:
import { Home, Star } from '@mui/icons-material';
// -----------------------------------------------
function IconExample() {
return (
<div>
<Home color="primary" />
<Star color="secondary" />
</div>
);
}
Ví dụ có thể kết hợp nhiều thành phần MUI thành một page đơn giản:
import { useState } from 'react';
import { AppBar, Toolbar, Typography, IconButton, Drawer, List, ListItem, ListItemText, Box, Grid, Card, CardContent, Button } from '@mui/material';
import MenuIcon from '@mui/icons-material/Menu';
// -----------------------------------------------------
function App() {
const [openDrawer, setOpenDrawer] = useState(false);
const toggleDrawer = () => {
setOpenDrawer(!openDrawer);
};
return (
<>
<AppBar position="static">
<Toolbar>
<IconButton color="inherit" edge="start" onClick={toggleDrawer}>
<MenuIcon />
</IconButton>
<Typography variant="h6" sx={{ flexGrow: 1 }}>
Trang chủ
</Typography>
<Button color="inherit">Đăng nhập</Button>
</Toolbar>
</AppBar>
<Drawer anchor="left" open={openDrawer} onClose={toggleDrawer}>
<Box sx={{ width: 250 }} role="presentation">
<List>
<ListItem button>
<ListItemText primary="Trang chủ" />
</ListItem>
<ListItem button>
<ListItemText primary="Giới thiệu" />
</ListItem>
<ListItem button>
<ListItemText primary="Liên hệ" />
</ListItem>
</List>
</Box>
</Drawer>
<Box sx={{ p: 2 }}>
<Typography variant="h4" gutterBottom>
Danh sách sản phẩm
</Typography>
<Grid container spacing={2}>
{Array.from({ length: 4 }).map((_, index) => (
<Grid item xs={12} sm={6} md={3} key={index}>
<Card sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
<CardContent>
<Typography variant="h5" gutterBottom>
Sản phẩm {index + 1}
</Typography>
<Typography variant="body2" color="text.secondary">
Mô tả ngắn về sản phẩm này.
</Typography>
</CardContent>
<Box sx={{ flexGrow: 1 }} />
<Box sx={{ p: 2 }}>
<Button variant="contained" color="primary" fullWidth>
Mua ngay
</Button>
</Box>
</Card>
</Grid>
))}
</Grid>
</Box>
</>
);
}
export default App;
4. Lưu ý khi sử dụng MUI
Đây chỉ là kinh nghiệm của cá nhân mình khi sử dụng MUI một thời gian, bạn nào thấy không phù hợp với bản thân thì có thể bỏ qua nha:
- Tree-shaking: MUI hỗ trợ tree-shaking, bạn nên import trực tiếp từ
@mui/material/Button
thay vì@mui/material
để giảm kích thước bundle. - SSR (Server-Side Rendering): nếu bạn dùng Next.js hay frameworks SSR, bạn cần cấu hình MUI đúng cách để tránh nhấp nháy nội dung (Flicker). Tài liệu MUI có hướng dẫn chi tiết về SSR.
- Custom theme theo module: bạn có thể chia nhỏ và quản lý theme trong nhiều file để dễ bảo trì.
- Migrate dần: nếu dự án lớn đã có CSS, bạn có thể kết hợp MUI và CSS cũ, sau đó migrate dần sang CSS-in-JS.
- MUI chỉ là layer UI. Hoàn toàn có thể kết hợp tối với các thư viện khác. Bạn vẫn có thể kết hợp với:
- React Router để điều hướng.
- React Query hay Redux để quản lý state.
- React Hook Form để tạo form
Đây là ví dụ mình thường hay sử dụng nhất là kết hợp TextField với React Hook Form như này:
import { useForm, Controller } from 'react-hook-form';
import { TextField, Button } from '@mui/material';
// ---------------------------------------------------
function FormExample() {
const { control, handleSubmit } = useForm();
const onSubmit = data => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="email"
control={control}
defaultValue=""
render={({ field }) => <TextField {...field} label="Email" variant="outlined" />}
/>
<Button type="submit" variant="contained">Send</Button>
</form>
);
}
5. Kết luận
MUI (Material UI) là một giải pháp toàn diện cho việc xây dựng giao diện React nhanh, đẹp và linh hoạt. Bộ components phong phú và khả năng tùy biến linh hoạt, MUI giúp các developer tiết kiệm thời gian, tạo ra sản phẩm có UI thống nhất.
Bằng cách nắm vững các khái niệm cơ bản về theming, sử dụng sx prop, styled, và các kỹ thuật override theme, bạn có thể dễ dàng tạo ra giao diện phù hợp với nhận diện thương hiệu, đồng thời đảm bảo trải nghiệm người dùng tốt trên nhiều thiết bị.
Hy vọng bài viết này giúp bạn có cái nhìn tổng quan và đầy đủ về MUI, từ cài đặt cơ bản đến sử dụng nâng cao.
Các bài viết liên quan:
Bài viết liên quan
Giới thiệu Ant Design: Hệ thống thiết kế UI dành cho Website
Dec 18, 2024 • 10 min read
Hướng dẫn tích hợp Sentry vào ứng dụng React
Dec 18, 2024 • 7 min read
Tìm hiểu Sentry: Công cụ Theo dõi Lỗi và Hiệu suất tự động
Dec 16, 2024 • 7 min read
Server-Side Rendering: Giải thích cơ chế hoạt động của SSR
Dec 11, 2024 • 15 min read
Client-Side Rendering: Giải thích cơ chế hoạt động của CSR
Dec 09, 2024 • 8 min read
Vercel là gì? Hướng dẫn deploy dự án Next.js bằng Vercel
Dec 07, 2024 • 14 min read