MUI (Material UI) Complete Guide | React Components and Theming
이 글의 핵심
MUI (formerly Material UI) is the most popular React component library — 90K+ GitHub stars, 3M weekly downloads. This guide covers MUI v6 from setup to custom themes and production patterns.
What This Guide Covers
MUI v6 provides a complete React component library with consistent design, full TypeScript support, and deep customization. This guide covers the essential components, theming, and patterns for building production UIs.
Real-world insight: Using MUI’s DataGrid cut a complex sortable/filterable table feature from 3 weeks of custom development to 2 hours of configuration.
Installation
npm install @mui/material @emotion/react @emotion/styled
npm install @mui/icons-material # optional: 2000+ icons
1. Basic Components
Button
import Button from '@mui/material/Button'
<Button variant="contained">Primary</Button>
<Button variant="outlined">Secondary</Button>
<Button variant="text">Text</Button>
<Button variant="contained" color="error" size="large">
Delete
</Button>
<Button variant="contained" startIcon={<DeleteIcon />} disabled>
Disabled
</Button>
TextField (Form Input)
import TextField from '@mui/material/TextField'
<TextField label="Email" type="email" fullWidth required />
<TextField
label="Password"
type="password"
error={!!errors.password}
helperText={errors.password?.message}
fullWidth
/>
<TextField
label="Bio"
multiline
rows={4}
fullWidth
placeholder="Tell us about yourself"
/>
Card
import Card from '@mui/material/Card'
import CardContent from '@mui/material/CardContent'
import CardActions from '@mui/material/CardActions'
<Card sx={{ maxWidth: 400 }}>
<CardMedia component="img" height="200" image="/cover.jpg" alt="cover" />
<CardContent>
<Typography variant="h5" component="h2">Card Title</Typography>
<Typography variant="body2" color="text.secondary">
Description text here
</Typography>
</CardContent>
<CardActions>
<Button size="small">Learn More</Button>
<Button size="small" color="error">Delete</Button>
</CardActions>
</Card>
2. Layout Components
Stack (Flexbox)
import Stack from '@mui/material/Stack'
<Stack direction="row" spacing={2} alignItems="center" justifyContent="space-between">
<Typography>Left</Typography>
<Button>Right</Button>
</Stack>
<Stack spacing={3}>
<TextField label="Name" />
<TextField label="Email" />
<Button variant="contained">Submit</Button>
</Stack>
Grid (Responsive Layout)
import Grid from '@mui/material/Grid'
<Grid container spacing={3}>
<Grid size={{ xs: 12, sm: 6, md: 4 }}>
<Card>Card 1</Card>
</Grid>
<Grid size={{ xs: 12, sm: 6, md: 4 }}>
<Card>Card 2</Card>
</Grid>
<Grid size={{ xs: 12, sm: 12, md: 4 }}>
<Card>Card 3</Card>
</Grid>
</Grid>
Container
import Container from '@mui/material/Container'
<Container maxWidth="lg">
{/* Content centered with max-width */}
</Container>
<Container maxWidth="sm">
{/* Narrow form container */}
</Container>
3. The sx Prop
The sx prop applies styles inline using MUI’s theme tokens:
<Box
sx={{
p: 3, // padding: theme.spacing(3) = 24px
m: 2, // margin
mt: 4, // margin-top
bgcolor: 'primary.main', // theme color
color: 'white',
borderRadius: 2, // theme.shape.borderRadius * 2
boxShadow: 3, // theme.shadows[3]
display: 'flex',
alignItems: 'center',
gap: 2,
// Responsive
fontSize: { xs: '1rem', md: '1.25rem' },
// Pseudo-classes
'&:hover': { bgcolor: 'primary.dark' },
// Dark mode
'.dark &': { bgcolor: 'grey.800' },
}}
>
Styled Box
</Box>
4. Custom Theme
// theme.ts
import { createTheme } from '@mui/material/styles'
export const theme = createTheme({
palette: {
primary: {
main: '#6366f1', // indigo
light: '#818cf8',
dark: '#4f46e5',
contrastText: '#fff',
},
secondary: {
main: '#ec4899', // pink
},
background: {
default: '#f8fafc',
paper: '#ffffff',
},
},
typography: {
fontFamily: '"Inter", "Roboto", sans-serif',
h1: { fontSize: '2.5rem', fontWeight: 700 },
h2: { fontSize: '2rem', fontWeight: 700 },
body1: { lineHeight: 1.7 },
},
shape: {
borderRadius: 8, // default for all components
},
components: {
// Override component defaults
MuiButton: {
styleOverrides: {
root: {
textTransform: 'none', // disable ALL_CAPS
fontWeight: 600,
},
containedPrimary: {
boxShadow: 'none',
'&:hover': { boxShadow: 'none' },
},
},
defaultProps: {
disableElevation: true,
},
},
MuiTextField: {
defaultProps: {
size: 'small',
variant: 'outlined',
},
},
},
})
// App.tsx
import { ThemeProvider, CssBaseline } from '@mui/material'
import { theme } from './theme'
export default function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline /> {/* normalize CSS */}
<Router />
</ThemeProvider>
)
}
5. Dark Mode
// useColorMode.ts
import { createTheme } from '@mui/material'
import { useState, useMemo } from 'react'
export function useColorMode() {
const [mode, setMode] = useState<'light' | 'dark'>('light')
const theme = useMemo(() => createTheme({
palette: {
mode,
...(mode === 'dark' ? {
background: { default: '#0f172a', paper: '#1e293b' },
primary: { main: '#818cf8' },
} : {
background: { default: '#f8fafc', paper: '#ffffff' },
primary: { main: '#6366f1' },
}),
},
}), [mode])
const toggleMode = () => setMode(m => m === 'light' ? 'dark' : 'light')
return { theme, mode, toggleMode }
}
// App.tsx
export default function App() {
const { theme, mode, toggleMode } = useColorMode()
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<IconButton onClick={toggleMode}>
{mode === 'dark' ? <LightModeIcon /> : <DarkModeIcon />}
</IconButton>
{/* rest of app */}
</ThemeProvider>
)
}
6. Dialog / Modal
import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material'
function ConfirmDialog({ open, onClose, onConfirm, message }) {
return (
<Dialog open={open} onClose={onClose} maxWidth="xs" fullWidth>
<DialogTitle>Confirm Action</DialogTitle>
<DialogContent>
<Typography>{message}</Typography>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>Cancel</Button>
<Button onClick={onConfirm} variant="contained" color="error">
Confirm
</Button>
</DialogActions>
</Dialog>
)
}
7. Select, Autocomplete, DatePicker
// Select
<FormControl fullWidth>
<InputLabel>Country</InputLabel>
<Select value={country} onChange={e => setCountry(e.target.value)} label="Country">
<MenuItem value="kr">South Korea</MenuItem>
<MenuItem value="us">United States</MenuItem>
<MenuItem value="jp">Japan</MenuItem>
</Select>
</FormControl>
// Autocomplete (searchable select)
import Autocomplete from '@mui/material/Autocomplete'
<Autocomplete
options={['React', 'Vue', 'Angular', 'Svelte']}
renderInput={(params) => <TextField {...params} label="Framework" />}
onChange={(_, value) => setFramework(value)}
/>
// Date Picker (requires @mui/x-date-pickers)
import { DatePicker } from '@mui/x-date-pickers'
<DatePicker label="Start Date" value={date} onChange={setDate} />
8. DataGrid (Advanced Table)
npm install @mui/x-data-grid
import { DataGrid } from '@mui/x-data-grid'
const columns = [
{ field: 'id', headerName: 'ID', width: 70 },
{ field: 'name', headerName: 'Name', width: 150, editable: true },
{ field: 'email', headerName: 'Email', width: 200 },
{ field: 'role', headerName: 'Role', width: 120,
type: 'singleSelect', valueOptions: ['admin', 'user', 'viewer'] },
{
field: 'actions',
type: 'actions',
getActions: ({ id }) => [
<GridActionsCellItem icon={<EditIcon />} label="Edit" onClick={() => handleEdit(id)} />,
<GridActionsCellItem icon={<DeleteIcon />} label="Delete" onClick={() => handleDelete(id)} />,
],
},
]
<DataGrid
rows={users}
columns={columns}
pageSizeOptions={[10, 25, 50]}
checkboxSelection
disableRowSelectionOnClick
onRowSelectionModelChange={setSelectedIds}
sx={{ border: 0 }}
/>
Key Takeaways
| Component | Use for |
|---|---|
Button | Actions, CTAs |
TextField | Forms, inputs |
Stack / Grid | Layout |
Card | Content containers |
Dialog | Modals, confirmations |
Autocomplete | Searchable selects |
DataGrid | Complex data tables |
sx prop | Quick inline theming |
createTheme | Global design system |
MUI’s strength is comprehensive coverage — every common UI pattern has a tested, accessible component. Customize via createTheme for global control, sx for one-off adjustments, and styleOverrides in the theme for component-level defaults. The sx prop alone eliminates most need for separate CSS files.