frontend / component libraries / materialui / components_and_theming.md

Material-UI Components and Theming

4 interview angles 9 min read source

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?” - sx for one-off adjustments at the call site, styled for a reusable styled component, theme styleOverrides for a rule that should apply to every instance. Reaching for sx everywhere is how a design system erodes into ad-hoc styling.
  • “How do you customise a component MUI does not expose a slot for?” - the slots and slotProps APIs, which replaced the older per-component components/componentsProps props. 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.