
Mui
- 47 installs
- 101 repo stars
- Updated November 28, 2025
- blencorp/claude-code-kit
MUI is a Claude Code skill that provides Material-UI v7 component, sx-prop styling, and theme patterns for React interfaces.
About
MUI is a skill that gives Claude Material-UI v7 patterns for component usage, sx prop styling, theme integration, and responsive design. A developer uses it when building UI with MUI components like Box, Grid, Paper, and Typography or customizing an MUI theme. It also documents v7 breaking changes from v6.
- Material-UI v7 sx prop styling and component patterns
- Theme integration via useTheme and sx theme tokens
- Documents v7 breaking changes from v6 (slots/slotProps, removed onBackdropClick)
Mui by the numbers
- 47 all-time installs (skills.sh)
- Ranked #1,322 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
mui capabilities & compatibility
- Capabilities
- ui design · frontend · component styling
- Use cases
- frontend · ui design
- IDEs
- vscode · cursor ide
What mui says it does
Material-UI v7 component library patterns including sx prop styling, theme integration, responsive design, and MUI-specific hooks.
Material-UI v7 (released March 2025) patterns for component usage, styling with sx prop, theme integration, and responsive design.
All components now use standardized `slots` and `slotProps` pattern
npx skills add https://github.com/blencorp/claude-code-kit --skill muiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 47 |
|---|---|
| repo stars | ★ 101 |
| Last updated | November 28, 2025 |
| Repository | blencorp/claude-code-kit ↗ |
What it does
Build and style React UI with Material-UI v7 components, the sx prop, and themes.
Who is it for?
Styling and composing React UI with Material-UI v7
Skip if: Non-MUI component libraries or backend work
When should I use this skill?
Working with MUI components, the sx prop, or theme customization
What you get
Components are styled with the sx prop and theme tokens using v7 conventions.
- Styled MUI components
- Theme configuration
By the numbers
- MUI v7 12-column grid system
Files
MUI v7 Patterns
Purpose
Material-UI v7 (released March 2025) patterns for component usage, styling with sx prop, theme integration, and responsive design.
Note: MUI v7 breaking changes from v6:
- Deep imports no longer work - use package exports field
onBackdropClickremoved from Modal - useonCloseinstead- All components now use standardized
slotsandslotPropspattern - CSS layers support via
enableCssLayerconfig (works with Tailwind v4)
When to Use This Skill
- Styling components with MUI sx prop
- Using MUI components (Box, Grid, Paper, Typography, etc.)
- Theme customization and usage
- Responsive design with MUI breakpoints
- MUI-specific utilities and hooks
---
Quick Start
Basic MUI Component
import { Box, Typography, Button, Paper } from '@mui/material';
import type { SxProps, Theme } from '@mui/material';
const styles: Record<string, SxProps<Theme>> = {
container: {
p: 2,
display: 'flex',
flexDirection: 'column',
gap: 2,
},
header: {
mb: 3,
fontSize: '1.5rem',
fontWeight: 600,
},
};
function MyComponent() {
return (
<Paper sx={styles.container}>
<Typography sx={styles.header}>
Title
</Typography>
<Button variant="contained">
Action
</Button>
</Paper>
);
}---
Styling Patterns
Inline Styles (< 100 lines)
For components with simple styling, define styles at the top:
import type { SxProps, Theme } from '@mui/material';
const componentStyles: Record<string, SxProps<Theme>> = {
container: {
p: 2,
display: 'flex',
flexDirection: 'column',
},
header: {
mb: 2,
color: 'primary.main',
},
button: {
mt: 'auto',
alignSelf: 'flex-end',
},
};
function Component() {
return (
<Box sx={componentStyles.container}>
<Typography sx={componentStyles.header}>Header</Typography>
<Button sx={componentStyles.button}>Action</Button>
</Box>
);
}Separate Styles File (>= 100 lines)
For complex components, create separate style file:
// UserProfile.styles.ts
import type { SxProps, Theme } from '@mui/material';
export const userProfileStyles: Record<string, SxProps<Theme>> = {
container: {
p: 3,
maxWidth: 800,
mx: 'auto',
},
header: {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
mb: 3,
},
// ... many more styles
};
// UserProfile.tsx
import { userProfileStyles as styles } from './UserProfile.styles';
function UserProfile() {
return <Box sx={styles.container}>...</Box>;
}---
Common Components
Layout Components
// Box - Generic container
<Box sx={{ p: 2, bgcolor: 'background.paper' }}>
Content
</Box>
// Paper - Elevated surface
<Paper elevation={2} sx={{ p: 3 }}>
Content
</Paper>
// Container - Centered content with max-width
<Container maxWidth="lg">
Content
</Container>
// Stack - Flex container with spacing
<Stack spacing={2} direction="row">
<Item />
<Item />
</Stack>Grid System
import { Grid } from '@mui/material';
// 12-column grid
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
Left half
</Grid>
<Grid item xs={12} md={6}>
Right half
</Grid>
</Grid>
// Responsive grid
<Grid container spacing={3}>
<Grid item xs={12} sm={6} md={4} lg={3}>
Card
</Grid>
{/* Repeat for more cards */}
</Grid>Typography
<Typography variant="h1">Heading 1</Typography>
<Typography variant="h2">Heading 2</Typography>
<Typography variant="body1">Body text</Typography>
<Typography variant="caption">Small text</Typography>
// With custom styling
<Typography
variant="h4"
sx={{
color: 'primary.main',
fontWeight: 600,
mb: 2,
}}
>
Custom Heading
</Typography>Buttons
// Variants
<Button variant="contained">Contained</Button>
<Button variant="outlined">Outlined</Button>
<Button variant="text">Text</Button>
// Colors
<Button variant="contained" color="primary">Primary</Button>
<Button variant="contained" color="secondary">Secondary</Button>
<Button variant="contained" color="error">Error</Button>
// With icons
import { Add as AddIcon } from '@mui/icons-material';
<Button startIcon={<AddIcon />}>Add Item</Button>---
Theme Integration
Using Theme Values
import { useTheme } from '@mui/material';
function Component() {
const theme = useTheme();
return (
<Box
sx={{
p: 2,
bgcolor: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
borderRadius: theme.shape.borderRadius,
}}
>
Themed box
</Box>
);
}Theme in sx Prop
<Box
sx={{
// Access theme in sx
color: 'primary.main', // theme.palette.primary.main
bgcolor: 'background.paper', // theme.palette.background.paper
p: 2, // theme.spacing(2)
borderRadius: 1, // theme.shape.borderRadius
}}
>
Content
</Box>
// Callback for advanced usage
<Box
sx={(theme) => ({
color: theme.palette.primary.main,
'&:hover': {
color: theme.palette.primary.dark,
},
})}
>
Hover me
</Box>---
Responsive Design
Breakpoints
// Mobile-first responsive values
<Box
sx={{
width: {
xs: '100%', // 0-600px
sm: '80%', // 600-900px
md: '60%', // 900-1200px
lg: '40%', // 1200-1536px
xl: '30%', // 1536px+
},
}}
>
Responsive width
</Box>
// Responsive display
<Box
sx={{
display: {
xs: 'none', // Hidden on mobile
md: 'block', // Visible on desktop
},
}}
>
Desktop only
</Box>Responsive Typography
<Typography
sx={{
fontSize: {
xs: '1rem',
md: '1.5rem',
lg: '2rem',
},
lineHeight: {
xs: 1.5,
md: 1.75,
},
}}
>
Responsive text
</Typography>---
Forms
import { TextField, Stack, Button } from '@mui/material';
<Box component="form" onSubmit={handleSubmit}>
<Stack spacing={2}>
<TextField
label="Email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
fullWidth
required
error={!!errors.email}
helperText={errors.email}
/>
<Button type="submit" variant="contained">Submit</Button>
</Stack>
</Box>---
Common Patterns
Card Component
import { Card, CardContent, CardActions, Typography, Button } from '@mui/material';
<Card>
<CardContent>
<Typography variant="h5" component="div">
Title
</Typography>
<Typography variant="body2" color="text.secondary">
Description
</Typography>
</CardContent>
<CardActions>
<Button size="small">Learn More</Button>
</CardActions>
</Card>Dialog/Modal
import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from '@mui/material';
<Dialog open={open} onClose={handleClose}>
<DialogTitle>Confirm Action</DialogTitle>
<DialogContent>
Are you sure you want to proceed?
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleConfirm} variant="contained">
Confirm
</Button>
</DialogActions>
</Dialog>Loading States
import { CircularProgress, Skeleton } from '@mui/material';
// Spinner
<Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}>
<CircularProgress />
</Box>
// Skeleton
<Stack spacing={1}>
<Skeleton variant="text" width="60%" />
<Skeleton variant="rectangular" height={200} />
<Skeleton variant="text" width="40%" />
</Stack>---
MUI-Specific Hooks
useMuiSnackbar
import { useMuiSnackbar } from '@/hooks/useMuiSnackbar';
function Component() {
const { showSuccess, showError, showInfo } = useMuiSnackbar();
const handleSave = async () => {
try {
await saveData();
showSuccess('Saved successfully');
} catch (error) {
showError('Failed to save');
}
};
return <Button onClick={handleSave}>Save</Button>;
}---
Icons
import { Add as AddIcon, Delete as DeleteIcon } from '@mui/icons-material';
import { Button, IconButton } from '@mui/material';
<Button startIcon={<AddIcon />}>Add</Button>
<IconButton onClick={handleDelete}><DeleteIcon /></IconButton>---
Best Practices
1. Type Your sx Props
import type { SxProps, Theme } from '@mui/material';
// ✅ Good
const styles: Record<string, SxProps<Theme>> = {
container: { p: 2 },
};
// ❌ Avoid
const styles = {
container: { p: 2 }, // No type safety
};2. Use Theme Tokens
// ✅ Good: Use theme tokens
<Box sx={{ color: 'primary.main', p: 2 }} />
// ❌ Avoid: Hardcoded values
<Box sx={{ color: '#1976d2', padding: '16px' }} />3. Consistent Spacing
// ✅ Good: Use spacing scale
<Box sx={{ p: 2, mb: 3, mt: 1 }} />
// ❌ Avoid: Random pixel values
<Box sx={{ padding: '17px', marginBottom: '25px' }} />---
Additional Resources
For more detailed patterns, see:
- styling-guide.md - Advanced styling patterns
- component-library.md - Component examples
- theme-customization.md - Theme setup
MUI Component Library
Layout Components
Box
The fundamental building block:
<Box
component="section"
sx={{
display: 'flex',
flexDirection: 'column',
gap: 2,
p: 3,
bgcolor: 'background.paper',
borderRadius: 2
}}
>
<Typography variant="h5">Section Title</Typography>
<Typography variant="body1">Content goes here</Typography>
</Box>Container
Centers content with max-width:
<Container maxWidth="lg" sx={{ py: 4 }}>
{/* Content automatically centered and constrained */}
</Container>Grid (v2)
Responsive grid layout:
import Grid from '@mui/material/Grid2';
<Grid container spacing={2}>
<Grid xs={12} sm={6} md={4}>
<Card>Item 1</Card>
</Grid>
<Grid xs={12} sm={6} md={4}>
<Card>Item 2</Card>
</Grid>
<Grid xs={12} sm={6} md={4}>
<Card>Item 3</Card>
</Grid>
</Grid>Stack
One-dimensional layout with spacing:
<Stack direction="row" spacing={2} alignItems="center">
<Avatar src={user.avatar} />
<Typography>{user.name}</Typography>
<Chip label={user.role} />
</Stack>Data Display
Typography
Text with theme variants:
<Typography variant="h1" component="h1" gutterBottom>
Main Heading
</Typography>
<Typography variant="body1" color="text.secondary">
Body text with secondary color
</Typography>
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
Bold caption
</Typography>Card
Content container:
<Card>
<CardMedia
component="img"
height="200"
image="/image.jpg"
alt="Description"
/>
<CardContent>
<Typography variant="h5" component="h2">
Card Title
</Typography>
<Typography variant="body2" color="text.secondary">
Card description text
</Typography>
</CardContent>
<CardActions>
<Button size="small">Learn More</Button>
<Button size="small">Share</Button>
</CardActions>
</Card>List
Structured content lists:
<List>
<ListItem disablePadding>
<ListItemButton onClick={handleClick}>
<ListItemIcon>
<InboxIcon />
</ListItemIcon>
<ListItemText
primary="Inbox"
secondary="5 new messages"
/>
</ListItemButton>
</ListItem>
<Divider />
<ListItem>
<ListItemAvatar>
<Avatar src="/avatar.jpg" />
</ListItemAvatar>
<ListItemText
primary="John Doe"
secondary="Online"
/>
</ListItem>
</List>Table
Data tables:
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell>Email</TableCell>
<TableCell align="right">Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow key={row.id} hover>
<TableCell>{row.name}</TableCell>
<TableCell>{row.email}</TableCell>
<TableCell align="right">
<IconButton size="small">
<EditIcon />
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>Input Components
TextField
Text input with validation:
<TextField
label="Email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
error={!!emailError}
helperText={emailError}
fullWidth
required
/>Select
Dropdown selection:
<FormControl fullWidth>
<InputLabel>Country</InputLabel>
<Select
value={country}
onChange={(e) => setCountry(e.target.value)}
label="Country"
>
<MenuItem value="us">United States</MenuItem>
<MenuItem value="uk">United Kingdom</MenuItem>
<MenuItem value="ca">Canada</MenuItem>
</Select>
</FormControl>Checkbox & Switch
Boolean inputs:
<FormControlLabel
control={<Checkbox checked={agreed} onChange={handleChange} />}
label="I agree to the terms"
/>
<FormControlLabel
control={<Switch checked={enabled} onChange={handleToggle} />}
label="Enable notifications"
/>Radio Group
Single selection:
<FormControl>
<FormLabel>Payment Method</FormLabel>
<RadioGroup value={payment} onChange={(e) => setPayment(e.target.value)}>
<FormControlLabel value="card" control={<Radio />} label="Credit Card" />
<FormControlLabel value="paypal" control={<Radio />} label="PayPal" />
<FormControlLabel value="crypto" control={<Radio />} label="Cryptocurrency" />
</RadioGroup>
</FormControl>Autocomplete
Searchable select:
<Autocomplete
options={options}
getOptionLabel={(option) => option.label}
value={value}
onChange={(event, newValue) => setValue(newValue)}
renderInput={(params) => (
<TextField {...params} label="Search" placeholder="Type to search..." />
)}
/>Buttons
Button Variants
<Stack direction="row" spacing={2}>
<Button variant="contained">Contained</Button>
<Button variant="outlined">Outlined</Button>
<Button variant="text">Text</Button>
</Stack>Button with Icons
<Button
variant="contained"
startIcon={<SaveIcon />}
onClick={handleSave}
>
Save Changes
</Button>
<IconButton color="primary">
<DeleteIcon />
</IconButton>
<Fab color="primary" sx={{ position: 'fixed', bottom: 16, right: 16 }}>
<AddIcon />
</Fab>Feedback Components
Alert
Status messages:
<Alert severity="success" onClose={handleClose}>
Operation completed successfully!
</Alert>
<Alert severity="error" icon={<ErrorIcon />}>
An error occurred. Please try again.
</Alert>Snackbar
Toast notifications:
<Snackbar
open={open}
autoHideDuration={6000}
onClose={handleClose}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
>
<Alert onClose={handleClose} severity="success">
Changes saved!
</Alert>
</Snackbar>Dialog
Modal dialogs:
<Dialog open={open} onClose={handleClose} maxWidth="sm" fullWidth>
<DialogTitle>Confirm Delete</DialogTitle>
<DialogContent>
<DialogContentText>
Are you sure you want to delete this item? This action cannot be undone.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleDelete} color="error" variant="contained">
Delete
</Button>
</DialogActions>
</Dialog>CircularProgress & LinearProgress
Loading indicators:
<CircularProgress />
<CircularProgress size={20} />
<CircularProgress variant="determinate" value={progress} />
<LinearProgress />
<LinearProgress variant="determinate" value={progress} />
<LinearProgress color="secondary" />Skeleton
Content placeholders:
<Stack spacing={1}>
<Skeleton variant="text" width="60%" height={40} />
<Skeleton variant="rectangular" width="100%" height={200} />
<Skeleton variant="circular" width={40} height={40} />
</Stack>Navigation
AppBar & Toolbar
Application header:
<AppBar position="static">
<Toolbar>
<IconButton edge="start" color="inherit">
<MenuIcon />
</IconButton>
<Typography variant="h6" sx={{ flexGrow: 1 }}>
App Title
</Typography>
<IconButton color="inherit">
<AccountCircle />
</IconButton>
</Toolbar>
</AppBar>Drawer
Side navigation:
<Drawer
anchor="left"
open={open}
onClose={handleClose}
>
<Box sx={{ width: 250 }} role="presentation">
<List>
<ListItem button onClick={() => navigate('/dashboard')}>
<ListItemIcon><DashboardIcon /></ListItemIcon>
<ListItemText primary="Dashboard" />
</ListItem>
<ListItem button onClick={() => navigate('/settings')}>
<ListItemIcon><SettingsIcon /></ListItemIcon>
<ListItemText primary="Settings" />
</ListItem>
</List>
</Box>
</Drawer>Tabs
Tabbed navigation:
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs value={tab} onChange={handleChange}>
<Tab label="Overview" />
<Tab label="Details" />
<Tab label="Settings" />
</Tabs>
</Box>
<TabPanel value={tab} index={0}>
Overview content
</TabPanel>Breadcrumbs
Navigation trail:
<Breadcrumbs>
<Link underline="hover" color="inherit" href="/">
Home
</Link>
<Link underline="hover" color="inherit" href="/products">
Products
</Link>
<Typography color="text.primary">Electronics</Typography>
</Breadcrumbs>Utility Components
Tooltip
Helpful hints:
<Tooltip title="Delete item" arrow placement="top">
<IconButton>
<DeleteIcon />
</IconButton>
</Tooltip>Popover
Floating content:
<Popover
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
>
<Box sx={{ p: 2 }}>
<Typography>Popover content</Typography>
</Box>
</Popover>Menu
Context menus:
<Menu
anchorEl={anchorEl}
open={open}
onClose={handleClose}
>
<MenuItem onClick={handleEdit}>
<ListItemIcon><EditIcon /></ListItemIcon>
<ListItemText>Edit</ListItemText>
</MenuItem>
<MenuItem onClick={handleDelete}>
<ListItemIcon><DeleteIcon /></ListItemIcon>
<ListItemText>Delete</ListItemText>
</MenuItem>
</Menu>Chip
Compact information:
<Stack direction="row" spacing={1}>
<Chip label="Active" color="success" />
<Chip label="Pending" color="warning" />
<Chip
label="Admin"
onDelete={handleDelete}
deleteIcon={<CloseIcon />}
/>
</Stack>Badge
Notification indicators:
<Badge badgeContent={4} color="error">
<MailIcon />
</Badge>
<Badge variant="dot" color="success">
<Avatar src="/avatar.jpg" />
</Badge>Avatar
User images:
<Stack direction="row" spacing={2}>
<Avatar src="/avatar.jpg" />
<Avatar sx={{ bgcolor: 'primary.main' }}>JD</Avatar>
<Avatar variant="square" src="/logo.png" />
<AvatarGroup max={3}>
<Avatar src="/user1.jpg" />
<Avatar src="/user2.jpg" />
<Avatar src="/user3.jpg" />
<Avatar src="/user4.jpg" />
</AvatarGroup>
</Stack>Form Example
Complete form with validation:
<Box component="form" onSubmit={handleSubmit} sx={{ maxWidth: 600 }}>
<Stack spacing={3}>
<TextField
label="Full Name"
required
fullWidth
error={!!errors.name}
helperText={errors.name}
/>
<TextField
label="Email"
type="email"
required
fullWidth
error={!!errors.email}
helperText={errors.email}
/>
<FormControl fullWidth>
<InputLabel>Role</InputLabel>
<Select label="Role" value={role} onChange={(e) => setRole(e.target.value)}>
<MenuItem value="user">User</MenuItem>
<MenuItem value="admin">Admin</MenuItem>
</Select>
</FormControl>
<FormControlLabel
control={<Checkbox checked={agreed} onChange={(e) => setAgreed(e.target.checked)} />}
label="I agree to the terms and conditions"
/>
<Stack direction="row" spacing={2} justifyContent="flex-end">
<Button variant="outlined" onClick={handleCancel}>
Cancel
</Button>
<Button type="submit" variant="contained" disabled={!agreed}>
Submit
</Button>
</Stack>
</Stack>
</Box>MUI Styling Guide
The sx Prop
The sx prop is the primary styling method in MUI v7+:
<Box
sx={{
p: 2, // padding: theme.spacing(2)
mb: 3, // marginBottom: theme.spacing(3)
bgcolor: 'primary.main', // theme.palette.primary.main
color: 'white',
borderRadius: 1, // theme.shape.borderRadius
'&:hover': {
bgcolor: 'primary.dark'
}
}}
/>Theme-Aware Values
// Spacing (multiplied by theme.spacing, default 8px)
sx={{ p: 2 }} // padding: 16px
// Colors from palette
sx={{ bgcolor: 'primary.main', color: 'text.secondary' }}
// Breakpoints
sx={{
width: { xs: '100%', sm: '50%', md: '33%' }
}}
// Typography variants
sx={{ typography: 'h4' }} // Applies theme.typography.h4Responsive Styles
<Box
sx={{
display: { xs: 'block', md: 'flex' },
flexDirection: { xs: 'column', md: 'row' },
gap: { xs: 1, sm: 2, md: 3 },
p: { xs: 2, sm: 3, md: 4 }
}}
/>Pseudo-Selectors and Nested Styles
<Button
sx={{
'&:hover': {
bgcolor: 'primary.dark'
},
'&:disabled': {
opacity: 0.5
},
'& .MuiButton-startIcon': {
mr: 1
}
}}
/>Dynamic Styles
interface CardProps {
isActive: boolean;
priority: 'low' | 'medium' | 'high';
}
function Card({ isActive, priority }: CardProps) {
return (
<Paper
sx={{
borderLeft: 4,
borderColor: isActive ? 'success.main' : 'grey.300',
bgcolor: priority === 'high' ? 'error.light' : 'background.paper',
opacity: isActive ? 1 : 0.7
}}
/>
);
}Global Styles with GlobalStyles
import { GlobalStyles } from '@mui/material';
<GlobalStyles
styles={{
body: {
margin: 0,
padding: 0
},
'*': {
boxSizing: 'border-box'
},
'.custom-scrollbar::-webkit-scrollbar': {
width: 8
}
}}
/>Styled Components API
For reusable styled components:
import { styled } from '@mui/material/styles';
const StyledCard = styled(Card)(({ theme }) => ({
padding: theme.spacing(2),
borderRadius: theme.shape.borderRadius * 2,
backgroundColor: theme.palette.background.paper,
transition: theme.transitions.create(['transform', 'box-shadow']),
'&:hover': {
transform: 'translateY(-4px)',
boxShadow: theme.shadows[8]
}
}));
// With props
interface StyledButtonProps {
variant: 'primary' | 'secondary';
}
const StyledButton = styled(Button)<StyledButtonProps>(({ theme, variant }) => ({
backgroundColor: variant === 'primary'
? theme.palette.primary.main
: theme.palette.secondary.main,
color: theme.palette.primary.contrastText,
'&:hover': {
backgroundColor: variant === 'primary'
? theme.palette.primary.dark
: theme.palette.secondary.dark
}
}));Theme Overrides
import { createTheme } from '@mui/material/styles';
const theme = createTheme({
components: {
MuiButton: {
styleOverrides: {
root: {
borderRadius: 8,
textTransform: 'none',
fontWeight: 600
},
containedPrimary: {
boxShadow: 'none',
'&:hover': {
boxShadow: 'none'
}
}
},
defaultProps: {
disableRipple: true
}
},
MuiCard: {
styleOverrides: {
root: {
borderRadius: 12,
boxShadow: '0 2px 8px rgba(0,0,0,0.1)'
}
}
}
}
});CSS Variables (MUI v7+)
// In theme configuration
const theme = createTheme({
cssVariables: true
});
// Use in sx prop
<Box sx={{ color: 'var(--mui-palette-primary-main)' }} />
// Use in CSS
.custom-element {
background-color: var(--mui-palette-background-paper);
padding: var(--mui-spacing-2);
}Common Patterns
Card with Hover Effect
<Card
sx={{
p: 3,
transition: 'all 0.3s',
'&:hover': {
transform: 'scale(1.02)',
boxShadow: 4
}
}}
/>Gradient Background
<Box
sx={{
background: (theme) =>
`linear-gradient(45deg, ${theme.palette.primary.main} 30%, ${theme.palette.secondary.main} 90%)`,
color: 'white'
}}
/>Sticky Header
<AppBar
sx={{
position: 'sticky',
top: 0,
zIndex: (theme) => theme.zIndex.drawer + 1
}}
/>Flexbox Layouts
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
gap: 2
}}
/>Performance Tips
- Use sx prop for dynamic styles - Better tree-shaking
- Use styled() for static reusable components - Better performance
- Avoid inline functions in sx - Can cause re-renders
- Leverage theme tokens - Consistent and performant
// ❌ Bad: Inline function in sx
<Box sx={() => ({ p: 2 })} />
// ✅ Good: Direct object
<Box sx={{ p: 2 }} />
// ✅ Good: Memoized function for complex logic
const getSx = useMemo(() => ({ p: 2, /* complex logic */ }), [deps]);
<Box sx={getSx} />Debugging Styles
// Inspect theme in console
<Box sx={(theme) => {
console.log('Theme:', theme);
return { p: 2 };
}} />
// Use sx as array for conditional styles
<Box sx={[
{ p: 2 },
isActive && { bgcolor: 'primary.main' },
isDisabled && { opacity: 0.5 }
]} />MUI Theme Customization
Creating a Custom Theme
import { createTheme, ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
const theme = createTheme({
palette: {
mode: 'light',
primary: {
main: '#1976d2',
light: '#42a5f5',
dark: '#1565c0',
contrastText: '#fff'
},
secondary: {
main: '#9c27b0',
light: '#ba68c8',
dark: '#7b1fa2',
contrastText: '#fff'
},
error: {
main: '#d32f2f'
},
warning: {
main: '#ed6c02'
},
info: {
main: '#0288d1'
},
success: {
main: '#2e7d32'
}
},
typography: {
fontFamily: '"Inter", "Roboto", "Helvetica", "Arial", sans-serif',
h1: {
fontSize: '2.5rem',
fontWeight: 600
},
h2: {
fontSize: '2rem',
fontWeight: 600
},
button: {
textTransform: 'none',
fontWeight: 600
}
},
shape: {
borderRadius: 8
},
spacing: 8
});
function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
{/* Your app */}
</ThemeProvider>
);
}Dark Mode Support
const getTheme = (mode: 'light' | 'dark') => createTheme({
palette: {
mode,
...(mode === 'light'
? {
// Light mode colors
primary: { main: '#1976d2' },
background: {
default: '#f5f5f5',
paper: '#ffffff'
}
}
: {
// Dark mode colors
primary: { main: '#90caf9' },
background: {
default: '#121212',
paper: '#1e1e1e'
}
})
}
});
function App() {
const [mode, setMode] = useState<'light' | 'dark'>('light');
const theme = useMemo(() => getTheme(mode), [mode]);
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<IconButton onClick={() => setMode(m => m === 'light' ? 'dark' : 'light')}>
{mode === 'light' ? <DarkModeIcon /> : <LightModeIcon />}
</IconButton>
{/* Your app */}
</ThemeProvider>
);
}Typography Customization
const theme = createTheme({
typography: {
fontFamily: '"Inter", sans-serif',
fontSize: 14,
fontWeightLight: 300,
fontWeightRegular: 400,
fontWeightMedium: 500,
fontWeightBold: 700,
h1: {
fontSize: '3rem',
fontWeight: 700,
lineHeight: 1.2,
letterSpacing: '-0.01562em'
},
h2: {
fontSize: '2.5rem',
fontWeight: 700,
lineHeight: 1.3
},
body1: {
fontSize: '1rem',
lineHeight: 1.5,
letterSpacing: '0.00938em'
},
button: {
fontSize: '0.875rem',
fontWeight: 600,
textTransform: 'none',
letterSpacing: '0.02857em'
}
}
});Component Style Overrides
const theme = createTheme({
components: {
MuiButton: {
styleOverrides: {
root: {
borderRadius: 8,
padding: '8px 16px',
boxShadow: 'none',
'&:hover': {
boxShadow: 'none'
}
},
contained: {
'&:hover': {
transform: 'translateY(-2px)',
transition: 'transform 0.2s'
}
}
},
defaultProps: {
disableElevation: true
}
},
MuiCard: {
styleOverrides: {
root: {
borderRadius: 12,
boxShadow: '0 2px 8px rgba(0,0,0,0.1)'
}
}
},
MuiTextField: {
defaultProps: {
variant: 'outlined',
size: 'medium'
},
styleOverrides: {
root: {
'& .MuiOutlinedInput-root': {
borderRadius: 8
}
}
}
},
MuiChip: {
styleOverrides: {
root: {
borderRadius: 6,
fontWeight: 500
}
}
}
}
});Breakpoints
const theme = createTheme({
breakpoints: {
values: {
xs: 0,
sm: 600,
md: 960,
lg: 1280,
xl: 1920
}
}
});
// Usage
<Box
sx={{
width: {
xs: '100%', // 0-599px
sm: '80%', // 600-959px
md: '60%', // 960-1279px
lg: '50%' // 1280px+
}
}}
/>Spacing
const theme = createTheme({
spacing: 8 // Base unit: 8px
});
// Usage: theme.spacing(2) = 16px
<Box sx={{ p: 2, m: 3 }} /> // padding: 16px, margin: 24pxCustom Palette Colors
declare module '@mui/material/styles' {
interface Palette {
tertiary: Palette['primary'];
}
interface PaletteOptions {
tertiary?: PaletteOptions['primary'];
}
}
const theme = createTheme({
palette: {
primary: {
main: '#1976d2'
},
secondary: {
main: '#9c27b0'
},
tertiary: {
main: '#ff9800',
light: '#ffb74d',
dark: '#f57c00',
contrastText: '#000'
}
}
});
// Usage
<Button color="tertiary">Tertiary Button</Button>Shadows
const theme = createTheme({
shadows: [
'none',
'0 2px 4px rgba(0,0,0,0.1)',
'0 4px 8px rgba(0,0,0,0.12)',
'0 8px 16px rgba(0,0,0,0.14)',
// ... up to shadows[24]
]
});
// Usage
<Paper elevation={2} /> // Uses shadows[2]Z-Index Layers
const theme = createTheme({
zIndex: {
mobileStepper: 1000,
speedDial: 1050,
appBar: 1100,
drawer: 1200,
modal: 1300,
snackbar: 1400,
tooltip: 1500
}
});Transitions
const theme = createTheme({
transitions: {
duration: {
shortest: 150,
shorter: 200,
short: 250,
standard: 300,
complex: 375,
enteringScreen: 225,
leavingScreen: 195
},
easing: {
easeInOut: 'cubic-bezier(0.4, 0, 0.2, 1)',
easeOut: 'cubic-bezier(0.0, 0, 0.2, 1)',
easeIn: 'cubic-bezier(0.4, 0, 1, 1)',
sharp: 'cubic-bezier(0.4, 0, 0.6, 1)'
}
}
});
// Usage
<Box
sx={{
transition: (theme) =>
theme.transitions.create(['transform', 'background-color'], {
duration: theme.transitions.duration.standard
})
}}
/>Complete Theme Example
import { createTheme } from '@mui/material/styles';
const theme = createTheme({
palette: {
mode: 'light',
primary: {
main: '#2563eb',
light: '#60a5fa',
dark: '#1e40af',
contrastText: '#ffffff'
},
secondary: {
main: '#7c3aed',
light: '#a78bfa',
dark: '#5b21b6',
contrastText: '#ffffff'
},
error: {
main: '#dc2626'
},
warning: {
main: '#f59e0b'
},
info: {
main: '#0ea5e9'
},
success: {
main: '#10b981'
},
background: {
default: '#f8fafc',
paper: '#ffffff'
},
text: {
primary: '#0f172a',
secondary: '#64748b'
}
},
typography: {
fontFamily: '"Inter", "Segoe UI", "Roboto", sans-serif',
fontSize: 14,
h1: {
fontSize: '3rem',
fontWeight: 700,
lineHeight: 1.2
},
h2: {
fontSize: '2.25rem',
fontWeight: 700,
lineHeight: 1.3
},
h3: {
fontSize: '1.875rem',
fontWeight: 600,
lineHeight: 1.4
},
h4: {
fontSize: '1.5rem',
fontWeight: 600,
lineHeight: 1.5
},
h5: {
fontSize: '1.25rem',
fontWeight: 600,
lineHeight: 1.6
},
h6: {
fontSize: '1rem',
fontWeight: 600,
lineHeight: 1.6
},
button: {
textTransform: 'none',
fontWeight: 600,
fontSize: '0.875rem'
}
},
shape: {
borderRadius: 10
},
spacing: 8,
components: {
MuiButton: {
styleOverrides: {
root: {
borderRadius: 8,
padding: '10px 20px',
fontWeight: 600,
boxShadow: 'none',
'&:hover': {
boxShadow: 'none'
}
},
contained: {
'&:hover': {
transform: 'translateY(-1px)',
boxShadow: '0 4px 12px rgba(0,0,0,0.15)'
}
}
},
defaultProps: {
disableElevation: true
}
},
MuiCard: {
styleOverrides: {
root: {
borderRadius: 12,
boxShadow: '0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24)',
'&:hover': {
boxShadow: '0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23)'
}
}
}
},
MuiTextField: {
defaultProps: {
variant: 'outlined'
},
styleOverrides: {
root: {
'& .MuiOutlinedInput-root': {
borderRadius: 8,
'&:hover .MuiOutlinedInput-notchedOutline': {
borderColor: '#2563eb'
}
}
}
}
},
MuiChip: {
styleOverrides: {
root: {
borderRadius: 6,
fontWeight: 500,
height: 28
}
}
},
MuiAppBar: {
styleOverrides: {
root: {
boxShadow: '0 1px 3px rgba(0,0,0,0.12)'
}
}
}
}
});
export default theme;Theme Provider Setup
// src/theme.ts - Export your theme
export { default as theme } from './theme';
// src/main.tsx or src/index.tsx
import { ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import theme from './theme';
root.render(
<StrictMode>
<ThemeProvider theme={theme}>
<CssBaseline />
<App />
</ThemeProvider>
</StrictMode>
);Accessing Theme in Components
import { useTheme } from '@mui/material/styles';
function MyComponent() {
const theme = useTheme();
return (
<Box
sx={{
color: theme.palette.primary.main,
padding: theme.spacing(2),
borderRadius: theme.shape.borderRadius
}}
/>
);
}Theme Nesting
import { createTheme, ThemeProvider, useTheme } from '@mui/material/styles';
function NestedTheme() {
const outerTheme = useTheme();
const innerTheme = createTheme({
...outerTheme,
palette: {
...outerTheme.palette,
mode: 'dark'
}
});
return (
<ThemeProvider theme={innerTheme}>
<Box sx={{ bgcolor: 'background.default', p: 2 }}>
Dark section within light app
</Box>
</ThemeProvider>
);
}{
"mui": {
"type": "domain",
"enforcement": "suggest",
"priority": "high",
"promptTriggers": {
"keywords": [
"mui",
"material-ui",
"material ui",
"@mui/material",
"@mui/icons-material",
"@mui/system",
"sx prop",
"mui theme",
"ThemeProvider",
"createTheme",
"useTheme",
"styled",
"Box",
"Typography",
"Button",
"TextField",
"Card",
"Dialog",
"Drawer",
"AppBar",
"Grid",
"Stack",
"Paper",
"Container",
"IconButton",
"Chip",
"Avatar",
"Alert",
"Snackbar",
"Menu",
"MenuItem",
"Select",
"Checkbox",
"Radio",
"Switch",
"Tabs",
"Autocomplete"
],
"intentPatterns": [
"style.*with.*mui",
"use.*mui.*component",
"create.*mui.*theme",
"customize.*mui",
"add.*material.*ui.*component",
"implement.*mui.*dialog",
"create.*mui.*form",
"setup.*mui.*theme",
"use.*mui.*icon",
"add.*mui.*(button|card|dialog|form|menu)"
]
},
"fileTriggers": {
"pathPatterns": [
"**/*.styles.ts",
"**/*.styles.tsx",
"**/components/**/*.tsx",
"**/components/**/*.ts",
"**/theme.ts",
"**/theme.tsx",
"**/theme/**/*.ts"
],
"contentPatterns": [
"import.*@mui/material",
"import.*@mui/icons-material",
"import.*@mui/system",
"from '@mui/material'",
"sx=\\{",
"<Box",
"<Typography",
"createTheme\\(",
"ThemeProvider",
"useTheme\\(\\)",
"styled\\("
]
}
}
}
Related skills
FAQ
Which MUI version does this cover?
Material-UI v7, released March 2025, including its breaking changes from v6.
How are components styled?
Primarily with the sx prop, using inline styles for small components and a separate styles file for larger ones.