components
MUI Core Components Reference
Input Components
TextField
The most common form input. Wraps FormControl, InputLabel, OutlinedInput/FilledInput/Input,
and FormHelperText in one component.
import TextField from '@mui/material/TextField';
import InputAdornment from '@mui/material/InputAdornment';
// Standard usage
<TextField
label="Email address"
type="email"
variant="outlined" // 'outlined' | 'filled' | 'standard'
size="small" // 'small' | 'medium'
fullWidth
required
value={email}
onChange={(e) => setEmail(e.target.value)}
error={!!emailError}
helperText={emailError || 'We will never share your email'}
InputProps={{
startAdornment: <InputAdornment position="start"><EmailIcon /></InputAdornment>,
}}
inputProps={{ maxLength: 100, 'aria-label': 'email address' }}
/>
// Multiline / textarea
<TextField
label="Description"
multiline
rows={4}
// or: minRows={2} maxRows={8} for auto-grow
fullWidth
/>
Autocomplete
Combines a text input with a dropdown for both free-form and constrained selection.
import Autocomplete from '@mui/material/Autocomplete';
import Chip from '@mui/material/Chip';
import CircularProgress from '@mui/material/CircularProgress';
// Static options
<Autocomplete
options={countries}
getOptionLabel={(option) => option.label}
isOptionEqualToValue={(option, value) => option.code === value.code}
value={selectedCountry}
onChange={(_, newValue) => setSelectedCountry(newValue)}
renderInput={(params) => (
<TextField {...params} label="Country" />
)}
/>
// Multiple selection with chips
<Autocomplete
multiple
options={tags}
value={selectedTags}
onChange={(_, newValue) => setSelectedTags(newValue)}
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip label={option} {...getTagProps({ index })} key={option} />
))
}
renderInput={(params) => (
<TextField {...params} label="Tags" placeholder="Add tag" />
)}
/>
// Async / server-side options
const [open, setOpen] = React.useState(false);
const [options, setOptions] = React.useState([]);
const [loading, setLoading] = React.useState(false);
<Autocomplete
open={open}
onOpen={() => { setOpen(true); fetchOptions(); }}
onClose={() => setOpen(false)}
options={options}
loading={loading}
renderInput={(params) => (
<TextField
{...params}
label="Search users"
InputProps={{
...params.InputProps,
endAdornment: (
<>
{loading && <CircularProgress size={20} />}
{params.InputProps.endAdornment}
</>
),
}}
/>
)}
/>
Select
import Select from '@mui/material/Select';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import InputLabel from '@mui/material/InputLabel';
import Checkbox from '@mui/material/Checkbox';
import ListItemText from '@mui/material/ListItemText';
<FormControl fullWidth size="small">
<InputLabel id="role-label">Role</InputLabel>
<Select
labelId="role-label"
label="Role"
value={role}
onChange={(e) => setRole(e.target.value)}
>
<MenuItem value="admin">Administrator</MenuItem>
<MenuItem value="editor">Editor</MenuItem>
<MenuItem value="viewer">Viewer</MenuItem>
</Select>
</FormControl>
// Multiple select with checkboxes
<Select
multiple
value={selectedRoles}
onChange={handleChange}
renderValue={(selected) => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{selected.map((value) => <Chip key={value} label={value} size="small" />)}
</Box>
)}
>
{roles.map((role) => (
<MenuItem key={role} value={role}>
<Checkbox checked={selectedRoles.includes(role)} />
<ListItemText primary={role} />
</MenuItem>
))}
</Select>
Checkbox, Radio, Switch
import Checkbox from '@mui/material/Checkbox';
import Radio from '@mui/material/Radio';
import RadioGroup from '@mui/material/RadioGroup';
import Switch from '@mui/material/Switch';
import FormControlLabel from '@mui/material/FormControlLabel';
// Checkbox with indeterminate state
<Checkbox
checked={allSelected}
indeterminate={someSelected && !allSelected}
onChange={handleSelectAll}
/>
// Radio group
<RadioGroup value={alignment} onChange={(e) => setAlignment(e.target.value)}>
<FormControlLabel value="left" control={<Radio />} label="Left" />
<FormControlLabel value="center" control={<Radio />} label="Center" />
<FormControlLabel value="right" control={<Radio />} label="Right" />
</RadioGroup>
// Switch
<FormControlLabel
control={
<Switch checked={darkMode} onChange={(e) => setDarkMode(e.target.checked)} />
}
label="Dark mode"
/>
Slider and Rating
import Slider from '@mui/material/Slider';
import Rating from '@mui/material/Rating';
// Range slider
<Slider
value={priceRange}
onChange={(_, newValue) => setPriceRange(newValue as number[])}
valueLabelDisplay="auto"
min={0}
max={1000}
step={10}
marks={[
{ value: 0, label: '$0' },
{ value: 500, label: '$500' },
{ value: 1000, label: '$1000' },
]}
/>
// Star rating
<Rating
value={rating}
onChange={(_, newValue) => setRating(newValue)}
precision={0.5}
size="large"
/>
Display Components
Typography
import Typography from '@mui/material/Typography';
// Semantic element with visual variant
<Typography variant="h1" component="h2">Page title</Typography>
// Caption with ellipsis
<Typography
variant="body2"
color="text.secondary"
noWrap
sx={{ maxWidth: 200 }}
>
Long text that will be truncated
</Typography>
// Paragraph with bottom margin
<Typography variant="body1" gutterBottom>
First paragraph with bottom margin.
</Typography>
Chip
import Chip from '@mui/material/Chip';
import Avatar from '@mui/material/Avatar';
<Chip label="Active" color="success" size="small" />
<Chip label="Draft" variant="outlined" onDelete={handleDelete} />
<Chip
avatar={<Avatar alt="User" src="/user.jpg" />}
label="Jane Smith"
onClick={handleClick}
clickable
/>
Avatar and Badge
import Avatar from '@mui/material/Avatar';
import AvatarGroup from '@mui/material/AvatarGroup';
import Badge from '@mui/material/Badge';
// Avatar with fallback initials
<Avatar src="/user.jpg" alt="John Doe">JD</Avatar>
// Avatar group with overflow count
<AvatarGroup max={4}>
{users.map((u) => <Avatar key={u.id} src={u.avatar} alt={u.name} />)}
</AvatarGroup>
// Notification badge on icon
<Badge badgeContent={unreadCount} color="error" max={99}>
<NotificationsIcon />
</Badge>
// Online indicator dot
<Badge
overlap="circular"
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
variant="dot"
color="success"
>
<Avatar src="/user.jpg" />
</Badge>
Tooltip
import Tooltip from '@mui/material/Tooltip';
<Tooltip title="Delete this item" placement="top" arrow>
<IconButton aria-label="delete"><DeleteIcon /></IconButton>
</Tooltip>
// Tooltip on disabled element (needs a wrapping span)
<Tooltip title="You don't have permission">
<span>
<Button disabled>Restricted action</Button>
</span>
</Tooltip>
Alert
import Alert from '@mui/material/Alert';
import AlertTitle from '@mui/material/AlertTitle';
<Alert severity="warning" variant="filled" onClose={handleClose}>
<AlertTitle>Warning</AlertTitle>
Your subscription expires in 3 days.
</Alert>
// severity: 'error' | 'warning' | 'info' | 'success'
// variant: 'standard' | 'filled' | 'outlined'
Table
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper from '@mui/material/Paper';
<TableContainer component={Paper} sx={{ maxHeight: 440 }}>
<Table stickyHeader aria-label="users table" size="small">
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell align="right">Age</TableCell>
<TableCell>Status</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow
key={row.id}
hover
selected={selectedId === row.id}
onClick={() => setSelectedId(row.id)}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell component="th" scope="row">{row.name}</TableCell>
<TableCell align="right">{row.age}</TableCell>
<TableCell>
<Chip
label={row.status}
color={row.status === 'active' ? 'success' : 'default'}
size="small"
/>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
Navigation Components
AppBar
import AppBar from '@mui/material/AppBar';
import Toolbar from '@mui/material/Toolbar';
import IconButton from '@mui/material/IconButton';
<AppBar position="sticky" color="default" elevation={1}>
<Toolbar>
<IconButton edge="start" onClick={toggleDrawer} aria-label="open menu">
<MenuIcon />
</IconButton>
<Typography variant="h6" sx={{ flexGrow: 1 }}>App Name</Typography>
<Button color="inherit" startIcon={<LoginIcon />}>Sign in</Button>
</Toolbar>
</AppBar>
Drawer
import Drawer from '@mui/material/Drawer';
import List from '@mui/material/List';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
// Temporary drawer (modal overlay)
<Drawer
anchor="left"
open={drawerOpen}
onClose={() => setDrawerOpen(false)}
PaperProps={{ sx: { width: 280 } }}
>
<Box role="presentation" onClick={() => setDrawerOpen(false)}>
<List>
{navItems.map((item) => (
<ListItemButton
key={item.path}
selected={pathname === item.path}
component={RouterLink}
to={item.path}
>
<ListItemIcon>{item.icon}</ListItemIcon>
<ListItemText primary={item.label} />
</ListItemButton>
))}
</List>
</Box>
</Drawer>
// Permanent drawer for desktop
<Drawer
variant="permanent"
sx={{ width: 240, '& .MuiDrawer-paper': { width: 240, boxSizing: 'border-box' } }}
>
{drawerContent}
</Drawer>
Tabs
import Tabs from '@mui/material/Tabs';
import Tab from '@mui/material/Tab';
<Tabs
value={activeTab}
onChange={(_, newValue) => setActiveTab(newValue)}
aria-label="main navigation tabs"
indicatorColor="primary"
textColor="primary"
variant="scrollable"
scrollButtons="auto"
>
<Tab label="Overview" value="overview" />
<Tab label="Analytics" value="analytics" />
<Tab label="Settings" value="settings" disabled />
</Tabs>
Menu
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import Divider from '@mui/material/Divider';
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
<IconButton onClick={(e) => setAnchorEl(e.currentTarget)} aria-label="account menu">
<AccountCircleIcon />
</IconButton>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={() => setAnchorEl(null)}
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
>
<MenuItem onClick={handleProfile}>
<ListItemIcon><PersonIcon /></ListItemIcon>
Profile
</MenuItem>
<Divider />
<MenuItem onClick={handleLogout} sx={{ color: 'error.main' }}>
<ListItemIcon><LogoutIcon color="error" /></ListItemIcon>
Logout
</MenuItem>
</Menu>
Breadcrumbs and Pagination
import Breadcrumbs from '@mui/material/Breadcrumbs';
import Pagination from '@mui/material/Pagination';
// Breadcrumbs
<Breadcrumbs aria-label="breadcrumb">
<Link component={RouterLink} to="/" underline="hover" color="inherit">Home</Link>
<Link component={RouterLink} to="/products" underline="hover" color="inherit">Products</Link>
<Typography color="text.primary">Laptop</Typography>
</Breadcrumbs>
// Pagination
<Pagination
count={totalPages}
page={currentPage}
onChange={(_, page) => setCurrentPage(page)}
color="primary"
size="small"
siblingCount={1}
boundaryCount={1}
showFirstButton
showLastButton
/>
Feedback Components
Dialog
import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogActions from '@mui/material/DialogActions';
<Dialog
open={open}
onClose={handleClose}
maxWidth="sm"
fullWidth
aria-labelledby="confirm-dialog-title"
>
<DialogTitle id="confirm-dialog-title">Confirm deletion</DialogTitle>
<DialogContent>
<DialogContentText>
Are you sure you want to delete <strong>{itemName}</strong>? This action cannot be undone.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleConfirm} color="error" variant="contained" autoFocus>
Delete
</Button>
</DialogActions>
</Dialog>
Snackbar
import Snackbar from '@mui/material/Snackbar';
import Alert from '@mui/material/Alert';
// Simple message
<Snackbar
open={snackbarOpen}
autoHideDuration={4000}
onClose={() => setSnackbarOpen(false)}
message="Changes saved"
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
/>
// With Alert for severity styling
<Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
<Alert onClose={handleClose} severity="success" variant="filled" sx={{ width: '100%' }}>
Profile updated successfully.
</Alert>
</Snackbar>
Progress and Loading States
import CircularProgress from '@mui/material/CircularProgress';
import LinearProgress from '@mui/material/LinearProgress';
import Backdrop from '@mui/material/Backdrop';
import Skeleton from '@mui/material/Skeleton';
// Indeterminate spinner
<CircularProgress size={24} />
// Determinate with percentage
<CircularProgress variant="determinate" value={uploadPercent} />
// Linear progress bar
<LinearProgress
variant="determinate"
value={progress}
sx={{ height: 8, borderRadius: 4 }}
/>
// Loading overlay
{loading && (
<Backdrop open sx={{ zIndex: (theme) => theme.zIndex.drawer + 1, color: '#fff' }}>
<CircularProgress color="inherit" />
</Backdrop>
)}
// Skeleton placeholder cards
{loading
? Array.from({ length: 6 }).map((_, i) => (
<Card key={i}>
<Skeleton variant="rectangular" height={140} />
<CardContent>
<Skeleton variant="text" width="80%" />
<Skeleton variant="text" width="60%" />
</CardContent>
</Card>
))
: items.map((item) => <ItemCard key={item.id} item={item} />)
}
Layout: Card and Paper
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import CardMedia from '@mui/material/CardMedia';
import CardActions from '@mui/material/CardActions';
import CardHeader from '@mui/material/CardHeader';
import Paper from '@mui/material/Paper';
<Card sx={{ maxWidth: 345 }} elevation={2}>
<CardHeader
avatar={<Avatar>{user.initials}</Avatar>}
title={user.name}
subheader={formatDate(post.createdAt)}
action={<IconButton aria-label="settings"><MoreVertIcon /></IconButton>}
/>
<CardMedia component="img" height="140" image={post.thumbnail} alt={post.title} />
<CardContent>
<Typography variant="body2" color="text.secondary">{post.excerpt}</Typography>
</CardContent>
<CardActions>
<Button size="small">Read more</Button>
<IconButton aria-label="like"><FavoriteIcon /></IconButton>
<IconButton aria-label="share"><ShareIcon /></IconButton>
</CardActions>
</Card>
// Paper as a surface container
<Paper elevation={0} variant="outlined" sx={{ p: 3, borderRadius: 2 }}>
{children}
</Paper>
Layout: List
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import ListSubheader from '@mui/material/ListSubheader';
<List
subheader={<ListSubheader component="div">Recent files</ListSubheader>}
sx={{ width: '100%', maxWidth: 360 }}
>
{files.map((file) => (
<React.Fragment key={file.id}>
<ListItem
disablePadding
secondaryAction={
<IconButton edge="end" onClick={() => handleDelete(file.id)}>
<DeleteIcon />
</IconButton>
}
>
<ListItemButton onClick={() => handleOpen(file)}>
<ListItemIcon><InsertDriveFileIcon /></ListItemIcon>
<ListItemText primary={file.name} secondary={formatBytes(file.size)} />
</ListItemButton>
</ListItem>
<Divider component="li" />
</React.Fragment>
))}
</List>
Utility Components
ClickAwayListener
Detects clicks outside the wrapped element — essential for custom dropdowns and popovers.
import ClickAwayListener from '@mui/material/ClickAwayListener';
<ClickAwayListener onClickAway={handleClose}>
<Box sx={{ position: 'relative' }}>
<Button onClick={toggleOpen}>Menu</Button>
{open && (
<Paper sx={{ position: 'absolute', top: '100%', zIndex: 1, width: 200 }}>
<MenuItem onClick={handleClose}>Option 1</MenuItem>
<MenuItem onClick={handleClose}>Option 2</MenuItem>
</Paper>
)}
</Box>
</ClickAwayListener>
Portal
Renders children into document.body or a custom container.
import Portal from '@mui/material/Portal';
<Portal>
<Box sx={{ position: 'fixed', bottom: 16, right: 16, zIndex: 'tooltip' }}>
<Fab color="primary"><AddIcon /></Fab>
</Box>
</Portal>
NoSsr — Client-Only Rendering
import NoSsr from '@mui/material/NoSsr';
<NoSsr fallback={<Skeleton variant="rectangular" height={300} />}>
<MapComponent /> {/* Depends on window — fails on server */}
</NoSsr>
useMediaQuery — Responsive Conditional Rendering
import useMediaQuery from '@mui/material/useMediaQuery';
import { useTheme } from '@mui/material/styles';
function ResponsiveNav() {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
return isMobile ? <MobileBottomNav /> : <DesktopSidebar />;
}
// Without theme — raw media query string
const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');
// With SSR safety — avoid hydration mismatch
const isDesktop = useMediaQuery(theme.breakpoints.up('lg'), {
noSsr: true, // skip server render for this query
defaultMatches: true, // assume true on server
// or: ssrMatchMedia for custom server-side matching
});
Component Prop (Polymorphic Rendering)
Most MUI components accept component to change the rendered HTML element or use a router link:
import { Link as RouterLink } from 'react-router-dom';
// Button as router link
<Button component={RouterLink} to="/dashboard" variant="contained">
Dashboard
</Button>
// ListItemButton as router link
<ListItemButton component={RouterLink} to="/settings">
<ListItemIcon><SettingsIcon /></ListItemIcon>
<ListItemText primary="Settings" />
</ListItemButton>
// Typography as a label
<Typography component="label" htmlFor="email-input" variant="body2">
Email Address
</Typography>
// Card as semantic article
<Card component="article">
<CardContent>...</CardContent>
</Card>
// Box as section with semantic HTML
<Box component="section" aria-labelledby="section-title">
<Typography id="section-title" variant="h2">Title</Typography>
</Box>
Global Configuration via Theme defaultProps
Set defaults for every instance of a component across the app:
const theme = createTheme({
components: {
MuiButton: {
defaultProps: {
variant: 'contained',
disableElevation: true,
size: 'medium',
},
},
MuiTextField: {
defaultProps: {
variant: 'outlined',
size: 'small',
fullWidth: true,
},
},
MuiTooltip: {
defaultProps: {
arrow: true,
enterDelay: 500,
},
},
MuiAlert: {
defaultProps: {
variant: 'filled',
},
},
MuiChip: {
defaultProps: {
size: 'small',
},
},
},
});
Now <Button>Save</Button> renders as contained by default — no need to repeat variant="contained" everywhere.
More from lobbi-docs/claude
vision-multimodal
Vision and multimodal capabilities for Claude including image analysis, PDF processing, and document understanding. Activate for image input, base64 encoding, multiple images, and visual analysis.
244design-system
Apply and manage the AI-powered design system with 50+ curated styles
126complex-reasoning
Multi-step reasoning patterns and frameworks for systematic problem solving. Activate for Chain-of-Thought, Tree-of-Thought, hypothesis-driven debugging, and structured analytical approaches that leverage extended thinking.
106gcp
Google Cloud Platform services including GKE, Cloud Run, Cloud Storage, BigQuery, and Pub/Sub. Activate for GCP infrastructure, Google Cloud deployment, and GCP integration.
73kanban
Kanban methodology including boards, WIP limits, flow metrics, and continuous delivery. Activate for Kanban boards, workflow visualization, and lean project management.
63debugging
Debugging techniques for Python, JavaScript, and distributed systems. Activate for troubleshooting, error analysis, log investigation, and performance debugging. Includes extended thinking integration for complex debugging scenarios.
59