Frontend / Component libraries / Material UI / components_and_theming.md

Material-UI Components and Theming

Updated 4 interview angles 9 min read source
On this page10
  1. Table of Contents
  2. Component Categories
  3. Advanced Theming
  4. Component Customization
  5. Responsive Components
  6. Accessibility Features
  7. Best Practices
  8. Common Interview Questions
  9. Summary
  10. Interview angle

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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
// 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

javascript
// 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

javascript
// 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 4

  • 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.