Skip to main content

Usage Guide

Learn essential patterns and best practices for using Material UI effectively in your React applications.

Component Imports

Material UI supports multiple import styles. Use one-level deep imports for optimal bundle size:
// Recommended: One-level deep imports
import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField';
import Card from '@mui/material/Card';

// Also works: Named imports from root
import { Button, TextField, Card } from '@mui/material';

// Icon imports
import DeleteIcon from '@mui/icons-material/Delete';
import SaveIcon from '@mui/icons-material/Save';
One-level deep imports (@mui/material/Button) provide better tree-shaking in package development, but both styles work well in applications.

The sx Prop

The sx prop is the most powerful styling solution in Material UI. It provides a shortcut for defining custom styles with direct access to the theme:

Basic Usage

import Box from '@mui/material/Box';

<Box
  sx={{
    width: 300,
    height: 200,
    backgroundColor: 'primary.main',
    '&:hover': {
      backgroundColor: 'primary.dark',
    },
  }}
/>

Theme-Aware Styling

Access theme values directly in the sx prop:
import Button from '@mui/material/Button';

<Button
  sx={{
    // Theme colors
    bgcolor: 'primary.main',
    color: 'primary.contrastText',
    
    // Theme spacing (multiplied by 8px by default)
    p: 2,              // padding: 16px
    m: 1,              // margin: 8px
    mb: 3,             // marginBottom: 24px
    
    // Responsive values
    width: { xs: '100%', sm: '50%', md: '33%' },
    
    // Theme breakpoints
    display: { xs: 'block', md: 'flex' },
  }}
/>

Responsive Design

import Typography from '@mui/material/Typography';

<Typography
  sx={{
    fontSize: {
      xs: '1rem',    // 0px and up
      sm: '1.25rem', // 600px and up
      md: '1.5rem',  // 900px and up
      lg: '2rem',    // 1200px and up
      xl: '2.5rem',  // 1536px and up
    },
    textAlign: { xs: 'center', md: 'left' },
  }}
>
  Responsive Typography
</Typography>

System Properties

The sx prop supports all CSS properties plus system shortcuts:
import Box from '@mui/material/Box';

<Box
  sx={{
    // Padding & Margin shortcuts
    p: 2,      // padding: 16px
    pt: 1,     // paddingTop: 8px
    px: 3,     // paddingLeft & paddingRight: 24px
    py: 2,     // paddingTop & paddingBottom: 16px
    m: 2,      // margin: 16px
    mt: 3,     // marginTop: 24px
    mx: 'auto', // marginLeft & marginRight: auto
    
    // Display
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'space-between',
    
    // Sizing
    width: 1,        // 100%
    minWidth: 300,
    maxWidth: 'sm',  // theme.breakpoints.values.sm
    height: '100vh',
    
    // Borders
    border: 1,
    borderColor: 'divider',
    borderRadius: 2,  // 8px
    
    // Shadows
    boxShadow: 3,
  }}
/>

TypeScript Support

Material UI provides comprehensive TypeScript definitions for all components.

Component Props

Import and use prop types for type-safe component development:
import Button from '@mui/material/Button';
import type { ButtonProps } from '@mui/material/Button';
import TextField from '@mui/material/TextField';
import type { TextFieldProps } from '@mui/material/TextField';

interface CustomButtonProps extends ButtonProps {
  label: string;
  loading?: boolean;
}

function CustomButton({ label, loading, ...props }: CustomButtonProps) {
  return (
    <Button disabled={loading} {...props}>
      {loading ? 'Loading...' : label}
    </Button>
  );
}

// TextField variants are fully typed
function CustomTextField(props: TextFieldProps<'outlined'>) {
  return <TextField variant="outlined" {...props} />;
}

Event Handlers

Event handlers are fully typed:
import * as React from 'react';
import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField';

function FormExample() {
  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    console.log('Button clicked:', event.currentTarget);
  };

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    console.log('Input value:', event.target.value);
  };

  return (
    <>
      <TextField onChange={handleChange} />
      <Button onClick={handleClick}>Submit</Button>
    </>
  );
}

Theme Typing

Extend the theme with custom properties:
import { createTheme } from '@mui/material/styles';

declare module '@mui/material/styles' {
  interface Theme {
    customColors: {
      brand: string;
      accent: string;
    };
  }
  interface ThemeOptions {
    customColors?: {
      brand?: string;
      accent?: string;
    };
  }
}

const theme = createTheme({
  customColors: {
    brand: '#0066cc',
    accent: '#ff6b35',
  },
});

// Use custom theme properties
<Box sx={{ bgcolor: (theme) => theme.customColors.brand }} />

Theming

Creating a Theme

Customize Material UI’s default theme to match your brand:
import { createTheme, ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';

const theme = createTheme({
  palette: {
    primary: {
      main: '#1976d2',
      light: '#42a5f5',
      dark: '#1565c0',
      contrastText: '#fff',
    },
    secondary: {
      main: '#dc004e',
      light: '#f73378',
      dark: '#9a0036',
      contrastText: '#fff',
    },
    error: {
      main: '#f44336',
    },
    warning: {
      main: '#ff9800',
    },
    info: {
      main: '#2196f3',
    },
    success: {
      main: '#4caf50',
    },
  },
  typography: {
    fontFamily: 'Roboto, Arial, sans-serif',
    h1: {
      fontSize: '2.5rem',
      fontWeight: 500,
    },
    button: {
      textTransform: 'none',
    },
  },
  spacing: 8, // Base spacing unit (default: 8px)
  shape: {
    borderRadius: 4, // Default border radius
  },
});

function App() {
  return (
    <ThemeProvider theme={theme}>
      <CssBaseline />
      {/* Your app */}
    </ThemeProvider>
  );
}

Dark Mode

Implement dark mode with theme mode toggling:
import * as React from 'react';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import Button from '@mui/material/Button';

function App() {
  const [mode, setMode] = React.useState<'light' | 'dark'>('light');

  const theme = React.useMemo(
    () =>
      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',
                },
              }),
        },
      }),
    [mode]
  );

  const toggleMode = () => {
    setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light'));
  };

  return (
    <ThemeProvider theme={theme}>
      <CssBaseline />
      <Button onClick={toggleMode}>
        Toggle {mode === 'light' ? 'Dark' : 'Light'} Mode
      </Button>
      {/* Your app */}
    </ThemeProvider>
  );
}

Layout Patterns

Container and Spacing

import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';

function PageLayout() {
  return (
    <Container maxWidth="lg">
      <Box sx={{ my: 4 }}>
        <Typography variant="h4" component="h1" gutterBottom>
          Page Title
        </Typography>
        <Typography variant="body1">
          Page content goes here
        </Typography>
      </Box>
    </Container>
  );
}

Grid Layout

import Grid from '@mui/material/Grid2';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';

function GridLayout() {
  return (
    <Grid container spacing={3}>
      <Grid size={{ xs: 12, sm: 6, md: 4 }}>
        <Card>
          <CardContent>Item 1</CardContent>
        </Card>
      </Grid>
      <Grid size={{ xs: 12, sm: 6, md: 4 }}>
        <Card>
          <CardContent>Item 2</CardContent>
        </Card>
      </Grid>
      <Grid size={{ xs: 12, sm: 6, md: 4 }}>
        <Card>
          <CardContent>Item 3</CardContent>
        </Card>
      </Grid>
    </Grid>
  );
}

Stack Layout

import Stack from '@mui/material/Stack';
import Button from '@mui/material/Button';

function ActionButtons() {
  return (
    <Stack direction="row" spacing={2}>
      <Button variant="contained">Save</Button>
      <Button variant="outlined">Cancel</Button>
      <Button>Reset</Button>
    </Stack>
  );
}

Form Handling

Controlled Inputs

import * as React from 'react';
import Box from '@mui/material/Box';
import TextField from '@mui/material/TextField';
import Button from '@mui/material/Button';
import Stack from '@mui/material/Stack';

interface FormData {
  name: string;
  email: string;
  message: string;
}

function ContactForm() {
  const [formData, setFormData] = React.useState<FormData>({
    name: '',
    email: '',
    message: '',
  });

  const handleChange = (field: keyof FormData) => (
    event: React.ChangeEvent<HTMLInputElement>
  ) => {
    setFormData({ ...formData, [field]: event.target.value });
  };

  const handleSubmit = (event: React.FormEvent) => {
    event.preventDefault();
    console.log('Form submitted:', formData);
  };

  return (
    <Box component="form" onSubmit={handleSubmit}>
      <Stack spacing={2}>
        <TextField
          label="Name"
          value={formData.name}
          onChange={handleChange('name')}
          required
          fullWidth
        />
        <TextField
          label="Email"
          type="email"
          value={formData.email}
          onChange={handleChange('email')}
          required
          fullWidth
        />
        <TextField
          label="Message"
          value={formData.message}
          onChange={handleChange('message')}
          multiline
          rows={4}
          required
          fullWidth
        />
        <Button type="submit" variant="contained" fullWidth>
          Submit
        </Button>
      </Stack>
    </Box>
  );
}

Form Validation

import * as React from 'react';
import TextField from '@mui/material/TextField';
import Button from '@mui/material/Button';
import Stack from '@mui/material/Stack';

function ValidatedForm() {
  const [email, setEmail] = React.useState('');
  const [password, setPassword] = React.useState('');
  const [errors, setErrors] = React.useState({
    email: '',
    password: '',
  });

  const validateEmail = (value: string) => {
    if (!value) return 'Email is required';
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
      return 'Invalid email address';
    }
    return '';
  };

  const validatePassword = (value: string) => {
    if (!value) return 'Password is required';
    if (value.length < 8) return 'Password must be at least 8 characters';
    return '';
  };

  const handleSubmit = (event: React.FormEvent) => {
    event.preventDefault();
    
    const emailError = validateEmail(email);
    const passwordError = validatePassword(password);
    
    setErrors({
      email: emailError,
      password: passwordError,
    });

    if (!emailError && !passwordError) {
      console.log('Form is valid');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <Stack spacing={2}>
        <TextField
          label="Email"
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          error={!!errors.email}
          helperText={errors.email}
          fullWidth
        />
        <TextField
          label="Password"
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          error={!!errors.password}
          helperText={errors.password}
          fullWidth
        />
        <Button type="submit" variant="contained" fullWidth>
          Sign In
        </Button>
      </Stack>
    </form>
  );
}

Performance Optimization

One-Level Imports

Use specific imports to reduce bundle size:
// Good: Specific imports
import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField';

// Acceptable: Named imports
import { Button, TextField } from '@mui/material';

Lazy Loading

Lazy load heavy components:
import * as React from 'react';

const HeavyComponent = React.lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <React.Suspense fallback={<div>Loading...</div>}>
      <HeavyComponent />
    </React.Suspense>
  );
}

Memoization

Memoize theme creation:
import * as React from 'react';
import { createTheme } from '@mui/material/styles';

function App() {
  const [mode, setMode] = React.useState<'light' | 'dark'>('light');
  
  // Theme is only recreated when mode changes
  const theme = React.useMemo(
    () =>
      createTheme({
        palette: { mode },
      }),
    [mode]
  );

  return <ThemeProvider theme={theme}>{/* ... */}</ThemeProvider>;
}

Next Steps

Component API Reference

Explore detailed API documentation for all components

Customization Guide

Learn advanced theming and customization techniques

Example Templates

Browse production-ready templates and examples

Migration Guides

Upgrade from previous versions of Material UI

Build docs developers (and LLMs) love