MUI (Material UI) Complete Guide | React Components and Theming

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

ComponentUse for
ButtonActions, CTAs
TextFieldForms, inputs
Stack / GridLayout
CardContent containers
DialogModals, confirmations
AutocompleteSearchable selects
DataGridComplex data tables
sx propQuick inline theming
createThemeGlobal 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.