Material-UI Components and Theming
This guide covers the comprehensive component system and advanced theming capabilities of Material-UI, including component categories, theming patterns, and customization techniques.
Table of Contents
Component Categories
1. Layout Components
AppBar & Navigation
import { AppBar, Toolbar, Typography, Button, IconButton } from '@mui/material';
import { Menu as MenuIcon } from '@mui/icons-material';
function NavigationBar() {
return (
<AppBar position="static">
<Toolbar>
<IconButton edge="start" color="inherit" aria-label="menu">
<MenuIcon />
</IconButton>
<Typography variant="h6" sx={{ flexGrow: 1 }}>
My App
</Typography>
<Button color="inherit">Login</Button>
</Toolbar>
</AppBar>
);
}
Grid System
import { Grid, Paper } from '@mui/material';
function GridLayout() {
return (
<Grid container spacing={3}>
<Grid item xs={12} sm={6} md={4}>
<Paper sx={{ p: 2 }}>Content 1</Paper>
</Grid>
<Grid item xs={12} sm={6} md={4}>
<Paper sx={{ p: 2 }}>Content 2</Paper>
</Grid>
<Grid item xs={12} md={4}>
<Paper sx={{ p: 2 }}>Content 3</Paper>
</Grid>
</Grid>
);
}
Container & Box
import { Container, Box } from '@mui/material';
function Layout() {
return (
<Container maxWidth="lg">
<Box sx={{
display: 'flex',
flexDirection: 'column',
minHeight: '100vh',
gap: 2
}}>
<Box component="header" sx={{ py: 2 }}>
Header
</Box>
<Box component="main" sx={{ flexGrow: 1 }}>
Main content
</Box>
<Box component="footer" sx={{ py: 2 }}>
Footer
</Box>
</Box>
</Container>
);
}
2. Input Components
Form Controls
import {
TextField,
Select,
MenuItem,
FormControl,
InputLabel,
Checkbox,
FormControlLabel,
Radio,
RadioGroup,
FormLabel
} from '@mui/material';
function FormExample() {
return (
<form>
<TextField
label="Full Name"
variant="outlined"
fullWidth
margin="normal"
required
/>
<FormControl fullWidth margin="normal">
<InputLabel>Country</InputLabel>
<Select label="Country">
<MenuItem value="us">United States</MenuItem>
<MenuItem value="ca">Canada</MenuItem>
<MenuItem value="uk">United Kingdom</MenuItem>
</Select>
</FormControl>
<FormControl component="fieldset" margin="normal">
<FormLabel component="legend">Gender</FormLabel>
<RadioGroup row>
<FormControlLabel value="female" control={<Radio />} label="Female" />
<FormControlLabel value="male" control={<Radio />} label="Male" />
<FormControlLabel value="other" control={<Radio />} label="Other" />
</RadioGroup>
</FormControl>
<FormControlLabel
control={<Checkbox />}
label="I agree to the terms and conditions"
/>
</form>
);
}
Advanced Input Components
import {
Autocomplete,
Slider,
Switch,
Rating,
ToggleButton,
ToggleButtonGroup
} from '@mui/material';
function AdvancedInputs() {
return (
<div>
<Autocomplete
options={['Option 1', 'Option 2', 'Option 3']}
renderInput={(params) => (
<TextField {...params} label="Autocomplete" />
)}
/>
<Slider
defaultValue={30}
aria-label="Volume"
valueLabelDisplay="auto"
step={10}
marks
min={10}
max={110}
/>
<Switch defaultChecked />
<Rating name="rating" defaultValue={2.5} precision={0.5} />
<ToggleButtonGroup value="left" exclusive>
<ToggleButton value="left">Left</ToggleButton>
<ToggleButton value="center">Center</ToggleButton>
<ToggleButton value="right">Right</ToggleButton>
</ToggleButtonGroup>
</div>
);
}
3. Data Display Components
Cards
import {
Card,
CardContent,
CardActions,
CardMedia,
Typography,
Button,
Avatar
} from '@mui/material';
function CardExample() {
return (
<Card sx={{ maxWidth: 345 }}>
<CardMedia
component="img"
height="140"
image="/static/images/cards/contemplative-reptile.jpg"
alt="green iguana"
/>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
Lizard
</Typography>
<Typography variant="body2" color="text.secondary">
Lizards are a widespread group of squamate reptiles, with over 6,000 species.
</Typography>
</CardContent>
<CardActions>
<Button size="small">Share</Button>
<Button size="small">Learn More</Button>
</CardActions>
</Card>
);
}
Tables
import {
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper
} from '@mui/material';
function TableExample() {
const rows = [
{ name: 'John Doe', age: 30, city: 'New York' },
{ name: 'Jane Smith', age: 25, city: 'Los Angeles' },
{ name: 'Bob Johnson', age: 35, city: 'Chicago' },
];
return (
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell>Age</TableCell>
<TableCell>City</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow key={row.name}>
<TableCell>{row.name}</TableCell>
<TableCell>{row.age}</TableCell>
<TableCell>{row.city}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
4. Feedback Components
Alerts & Notifications
import {
Alert,
Snackbar,
AlertTitle,
CircularProgress,
LinearProgress,
Skeleton
} from '@mui/material';
function FeedbackExample() {
return (
<div>
<Alert severity="error">
<AlertTitle>Error</AlertTitle>
This is an error alert — check it out!
</Alert>
<Alert severity="warning">This is a warning alert!</Alert>
<Alert severity="info">This is an info alert!</Alert>
<Alert severity="success">This is a success alert!</Alert>
<CircularProgress />
<LinearProgress />
<Skeleton variant="rectangular" width={210} height={118} />
<Skeleton variant="text" />
<Skeleton variant="circular" width={40} height={40} />
</div>
);
}
Advanced Theming
Theme Structure
import { createTheme } from '@mui/material/styles';
const theme = createTheme({
palette: {
mode: 'light', // or 'dark'
primary: {
main: '#1976d2',
light: '#42a5f5',
dark: '#1565c0',
contrastText: '#fff',
},
secondary: {
main: '#dc004e',
light: '#ff5983',
dark: '#9a0036',
contrastText: '#fff',
},
background: {
default: '#fafafa',
paper: '#fff',
},
text: {
primary: 'rgba(0, 0, 0, 0.87)',
secondary: 'rgba(0, 0, 0, 0.6)',
},
},
typography: {
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
h1: {
fontSize: '2.5rem',
fontWeight: 500,
lineHeight: 1.2,
},
h2: {
fontSize: '2rem',
fontWeight: 500,
lineHeight: 1.3,
},
body1: {
fontSize: '1rem',
lineHeight: 1.5,
},
button: {
textTransform: 'none',
fontWeight: 500,
},
},
shape: {
borderRadius: 8,
},
spacing: 8,
breakpoints: {
values: {
xs: 0,
sm: 600,
md: 900,
lg: 1200,
xl: 1536,
},
},
components: {
MuiButton: {
styleOverrides: {
root: {
borderRadius: 8,
textTransform: 'none',
},
contained: {
boxShadow: 'none',
'&:hover': {
boxShadow: '0px 2px 4px rgba(0,0,0,0.2)',
},
},
},
variants: [
{
props: { variant: 'gradient' },
style: {
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
color: 'white',
},
},
],
},
MuiCard: {
styleOverrides: {
root: {
boxShadow: '0px 2px 8px rgba(0,0,0,0.1)',
},
},
},
},
});
Dark Mode Theme
import { createTheme, ThemeProvider } from '@mui/material/styles';
import { useState } from 'react';
function App() {
const [mode, setMode] = useState('light');
const theme = createTheme({
palette: {
mode,
...(mode === 'light'
? {
// Light mode colors
primary: {
main: '#1976d2',
},
background: {
default: '#fafafa',
paper: '#fff',
},
}
: {
// Dark mode colors
primary: {
main: '#90caf9',
},
background: {
default: '#121212',
paper: '#1e1e1e',
},
}),
},
});
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Button onClick={() => setMode(mode === 'light' ? 'dark' : 'light')}>
Toggle {mode === 'light' ? 'Dark' : 'Light'} Mode
</Button>
<YourApp />
</ThemeProvider>
);
}
Custom Color Palette
const theme = createTheme({
palette: {
primary: {
50: '#e3f2fd',
100: '#bbdefb',
200: '#90caf9',
300: '#64b5f6',
400: '#42a5f5',
500: '#2196f3',
600: '#1e88e5',
700: '#1976d2',
800: '#1565c0',
900: '#0d47a1',
A100: '#82b1ff',
A200: '#448aff',
A400: '#2979ff',
A700: '#2962ff',
},
custom: {
main: '#ff6b6b',
light: '#ff8e8e',
dark: '#e55a5a',
},
},
});
Component Customization
Styled Components
import { styled } from '@mui/material/styles';
import { Button, Card } from '@mui/material';
const CustomButton = styled(Button)(({ theme }) => ({
background: `linear-gradient(45deg, ${theme.palette.primary.main} 30%, ${theme.palette.secondary.main} 90%)`,
border: 0,
borderRadius: 15,
boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
color: 'white',
height: 48,
padding: '0 30px',
'&:hover': {
background: `linear-gradient(45deg, ${theme.palette.primary.dark} 30%, ${theme.palette.secondary.dark} 90%)`,
},
}));
const CustomCard = styled(Card)(({ theme }) => ({
position: 'relative',
backgroundColor: theme.palette.grey[800],
color: theme.palette.common.white,
marginBottom: theme.spacing(4),
backgroundImage: 'url(https://source.unsplash.com/random)',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
'&::before': {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0,0,0,.3)',
content: '""',
},
}));
Sx Prop Examples
import { Box, Button } from '@mui/material';
function SxExamples() {
return (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
gap: 2,
p: 3,
bgcolor: 'background.paper',
borderRadius: 2,
boxShadow: 1,
'&:hover': {
boxShadow: 3,
},
'@media (min-width: 600px)': {
flexDirection: 'row',
},
}}
>
<Button
sx={{
bgcolor: 'primary.main',
color: 'white',
'&:hover': {
bgcolor: 'primary.dark',
},
'&.Mui-disabled': {
bgcolor: 'grey.300',
},
}}
>
Custom Button
</Button>
</Box>
);
}
Responsive Components
Responsive Grid
import { Grid, Paper } from '@mui/material';
function ResponsiveGrid() {
return (
<Grid container spacing={3}>
<Grid item xs={12} sm={6} md={4} lg={3}>
<Paper sx={{ p: 2, textAlign: 'center' }}>
xs=12 sm=6 md=4 lg=3
</Paper>
</Grid>
<Grid item xs={12} sm={6} md={4} lg={3}>
<Paper sx={{ p: 2, textAlign: 'center' }}>
xs=12 sm=6 md=4 lg=3
</Paper>
</Grid>
<Grid item xs={12} sm={6} md={4} lg={3}>
<Paper sx={{ p: 2, textAlign: 'center' }}>
xs=12 sm=6 md=4 lg=3
</Paper>
</Grid>
<Grid item xs={12} sm={6} md={4} lg={3}>
<Paper sx={{ p: 2, textAlign: 'center' }}>
xs=12 sm=6 md=4 lg=3
</Paper>
</Grid>
</Grid>
);
}
Responsive Typography
import { Typography, Box } from '@mui/material';
function ResponsiveTypography() {
return (
<Box>
<Typography
variant="h1"
sx={{
fontSize: {
xs: '2rem',
sm: '3rem',
md: '4rem',
lg: '5rem',
},
}}
>
Responsive Heading
</Typography>
<Typography
sx={{
fontSize: {
xs: '0.875rem',
sm: '1rem',
md: '1.125rem',
},
lineHeight: {
xs: 1.4,
sm: 1.5,
md: 1.6,
},
}}
>
Responsive body text
</Typography>
</Box>
);
}
Accessibility Features
ARIA Support
import { Button, TextField, Alert } from '@mui/material';
function AccessibleComponents() {
return (
<div>
<TextField
label="Email"
type="email"
aria-describedby="email-helper"
helperText="Enter your email address"
required
inputProps={{
'aria-label': 'Email address input',
}}
/>
<Button
aria-label="Submit form"
aria-describedby="submit-description"
>
Submit
</Button>
<Alert
severity="info"
aria-live="polite"
role="alert"
>
This is an informational message
</Alert>
</div>
);
}
Focus Management
import { Button, Dialog, DialogTitle, DialogContent } from '@mui/material';
import { useRef } from 'react';
function FocusManagement() {
const buttonRef = useRef(null);
return (
<div>
<Button ref={buttonRef}>
Open Dialog
</Button>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="dialog-title"
aria-describedby="dialog-description"
>
<DialogTitle id="dialog-title">
Dialog Title
</DialogTitle>
<DialogContent>
Dialog content
</DialogContent>
</Dialog>
</div>
);
}
Best Practices
1. Component Composition
// Good: Compose components
function UserCard({ user }) {
return (
<Card>
<CardContent>
<Box display="flex" alignItems="center" gap={2}>
<Avatar src={user.avatar} />
<Box>
<Typography variant="h6">{user.name}</Typography>
<Typography variant="body2" color="text.secondary">
{user.email}
</Typography>
</Box>
</Box>
</CardContent>
</Card>
);
}
2. Theme Consistency
// Use theme values consistently
const theme = createTheme({
palette: {
primary: {
main: '#1976d2',
},
},
typography: {
h1: {
fontSize: '2.5rem',
fontWeight: 500,
},
},
spacing: 8,
});
// Use theme.spacing() for consistent spacing
<Box sx={{ p: theme.spacing(2), m: theme.spacing(1) }}>
Content
</Box>
3. Performance Optimization
// Use React.memo for expensive components
const ExpensiveComponent = React.memo(({ data }) => {
return (
<List>
{data.map(item => (
<ListItem key={item.id}>
{item.name}
</ListItem>
))}
</List>
);
});
// Use useMemo for expensive calculations
const expensiveValue = useMemo(() => {
return computeExpensiveValue(data);
}, [data]);
Common Interview Questions
Q: How do you customize Material-UI components?
- Through theme customization, styled components, sx prop, or component styleOverrides
Q: What’s the difference between sx prop and styled components?
- sx prop is for one-off styling, styled components are for reusable styled components
Q: How do you implement dark mode in Material-UI?
- Create a theme with mode: ‘dark’ and use ThemeProvider to wrap your app
Q: How do you handle responsive design in Material-UI?
- Using the Grid system with breakpoint props and responsive values in sx prop
Q: What are the accessibility features in Material-UI?
- Built-in ARIA attributes, keyboard navigation, screen reader support, and WCAG compliance
Q: How do you optimize Material-UI performance?
- Use React.memo, useMemo, and avoid unnecessary re-renders
Summary
- Material-UI provides a comprehensive component library with consistent APIs
- Advanced theming system allows for deep customization
- Built-in accessibility features ensure inclusive design
- Responsive design is handled through Grid system and responsive values
- Performance optimization techniques help maintain smooth user experience
- Best practices ensure maintainable and scalable code
Interview angle
- “
sx,styled, or the theme?” -sxfor one-off adjustments at the call site,styledfor a reusable styled component, themestyleOverridesfor a rule that should apply to every instance. Reaching forsxeverywhere is how a design system erodes into ad-hoc styling. - “How do you customise a component MUI does not expose a slot for?” - the
slotsandslotPropsAPIs, which replaced the older per-componentcomponents/componentsPropsprops. They let you swap or configure internal elements without forking the component. - “How do you keep the bundle reasonable?” - named imports from the package root are tree-shaken by modern bundlers; the older deep-import advice is mostly obsolete but still helps development build times. Icons are the real weight - import them individually.
- “When would you not use MUI?” - when the design is not Material and you will fight the defaults, or when you need full control over markup and accessibility primitives - in which case a headless library plus your own styles is less work.