Material-UI Advanced Theming and Components

4 interview angles 6 min read source

Material-UI Advanced Theming and Components

This guide covers advanced theming techniques, component customization, and best practices for Material-UI applications.

Table of Contents


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 Design

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

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

  • “How do you theme MUI beyond colours?” - the theme object carries palette, typography, spacing, breakpoints and components, where defaultProps and styleOverrides change every instance of a component. Overriding at the theme level is what keeps one-off sx props from spreading through the codebase.
  • “How do you support dark mode without a flash?” - the CSS-variables theme, which emits custom properties and switches with a data attribute rather than re-rendering a new theme object. That also makes SSR work without a hydration mismatch.
  • “How do you extend the theme in TypeScript?” - module augmentation on MUI’s theme interfaces, so custom palette keys and variants type-check. Without it, custom keys are a runtime-only convention.
  • “What is the performance concern?” - creating the theme inside a component recreates it every render and invalidates the whole styling cache. Build it once at module level with createTheme and memoize any dynamic parts.