--- name: components description: MUI core component patterns and best practices triggers: - component - Button - TextField - Dialog - Table - AppBar - Drawer - Autocomplete - Snackbar allowed-tools: - Read - Glob - Grep - Write - Edit globs: - "*.tsx" - "*.jsx" --- # MUI Core Components Reference ## Input Components ### TextField The most common form input. Wraps `FormControl`, `InputLabel`, `OutlinedInput`/`FilledInput`/`Input`, and `FormHelperText` in one component. ```tsx import TextField from '@mui/material/TextField'; import InputAdornment from '@mui/material/InputAdornment'; // Standard usage setEmail(e.target.value)} error={!!emailError} helperText={emailError || 'We will never share your email'} InputProps={{ startAdornment: , }} inputProps={{ maxLength: 100, 'aria-label': 'email address' }} /> // Multiline / textarea ``` ### Autocomplete Combines a text input with a dropdown for both free-form and constrained selection. ```tsx import Autocomplete from '@mui/material/Autocomplete'; import Chip from '@mui/material/Chip'; import CircularProgress from '@mui/material/CircularProgress'; // Static options option.label} isOptionEqualToValue={(option, value) => option.code === value.code} value={selectedCountry} onChange={(_, newValue) => setSelectedCountry(newValue)} renderInput={(params) => ( )} /> // Multiple selection with chips setSelectedTags(newValue)} renderTags={(value, getTagProps) => value.map((option, index) => ( )) } renderInput={(params) => ( )} /> // Async / server-side options const [open, setOpen] = React.useState(false); const [options, setOptions] = React.useState([]); const [loading, setLoading] = React.useState(false); { setOpen(true); fetchOptions(); }} onClose={() => setOpen(false)} options={options} loading={loading} renderInput={(params) => ( {loading && } {params.InputProps.endAdornment} ), }} /> )} /> ``` ### Select ```tsx 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'; Role // Multiple select with checkboxes ``` ### Checkbox, Radio, Switch ```tsx 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 // Radio group setAlignment(e.target.value)}> } label="Left" /> } label="Center" /> } label="Right" /> // Switch setDarkMode(e.target.checked)} /> } label="Dark mode" /> ``` ### Slider and Rating ```tsx import Slider from '@mui/material/Slider'; import Rating from '@mui/material/Rating'; // Range slider 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 setRating(newValue)} precision={0.5} size="large" /> ``` --- ## Display Components ### Typography ```tsx import Typography from '@mui/material/Typography'; // Semantic element with visual variant Page title // Caption with ellipsis Long text that will be truncated // Paragraph with bottom margin First paragraph with bottom margin. ``` ### Chip ```tsx import Chip from '@mui/material/Chip'; import Avatar from '@mui/material/Avatar'; } label="Jane Smith" onClick={handleClick} clickable /> ``` ### Avatar and Badge ```tsx import Avatar from '@mui/material/Avatar'; import AvatarGroup from '@mui/material/AvatarGroup'; import Badge from '@mui/material/Badge'; // Avatar with fallback initials JD // Avatar group with overflow count {users.map((u) => )} // Notification badge on icon // Online indicator dot ``` ### Tooltip ```tsx import Tooltip from '@mui/material/Tooltip'; // Tooltip on disabled element (needs a wrapping span) ``` ### Alert ```tsx import Alert from '@mui/material/Alert'; import AlertTitle from '@mui/material/AlertTitle'; Warning Your subscription expires in 3 days. // severity: 'error' | 'warning' | 'info' | 'success' // variant: 'standard' | 'filled' | 'outlined' ``` ### Table ```tsx 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'; Name Age Status {rows.map((row) => ( setSelectedId(row.id)} sx={{ '&:last-child td, &:last-child th': { border: 0 } }} > {row.name} {row.age} ))}
``` --- ## Navigation Components ### AppBar ```tsx import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; import IconButton from '@mui/material/IconButton'; App Name ``` ### Drawer ```tsx 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) setDrawerOpen(false)} PaperProps={{ sx: { width: 280 } }} > setDrawerOpen(false)}> {navItems.map((item) => ( {item.icon} ))} // Permanent drawer for desktop {drawerContent} ``` ### Tabs ```tsx import Tabs from '@mui/material/Tabs'; import Tab from '@mui/material/Tab'; setActiveTab(newValue)} aria-label="main navigation tabs" indicatorColor="primary" textColor="primary" variant="scrollable" scrollButtons="auto" > ``` ### Menu ```tsx import Menu from '@mui/material/Menu'; import MenuItem from '@mui/material/MenuItem'; import Divider from '@mui/material/Divider'; const [anchorEl, setAnchorEl] = React.useState(null); setAnchorEl(e.currentTarget)} aria-label="account menu"> setAnchorEl(null)} transformOrigin={{ horizontal: 'right', vertical: 'top' }} anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} > Profile Logout ``` ### Breadcrumbs and Pagination ```tsx import Breadcrumbs from '@mui/material/Breadcrumbs'; import Pagination from '@mui/material/Pagination'; // Breadcrumbs Home Products Laptop // Pagination setCurrentPage(page)} color="primary" size="small" siblingCount={1} boundaryCount={1} showFirstButton showLastButton /> ``` --- ## Feedback Components ### Dialog ```tsx 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'; Confirm deletion Are you sure you want to delete {itemName}? This action cannot be undone. ``` ### Snackbar ```tsx import Snackbar from '@mui/material/Snackbar'; import Alert from '@mui/material/Alert'; // Simple message setSnackbarOpen(false)} message="Changes saved" anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} /> // With Alert for severity styling Profile updated successfully. ``` ### Progress and Loading States ```tsx 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 // Determinate with percentage // Linear progress bar // Loading overlay {loading && ( theme.zIndex.drawer + 1, color: '#fff' }}> )} // Skeleton placeholder cards {loading ? Array.from({ length: 6 }).map((_, i) => ( )) : items.map((item) => ) } ``` --- ## Layout: Card and Paper ```tsx 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'; {user.initials}} title={user.name} subheader={formatDate(post.createdAt)} action={} /> {post.excerpt} // Paper as a surface container {children} ``` ## Layout: List ```tsx 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'; Recent files} sx={{ width: '100%', maxWidth: 360 }} > {files.map((file) => ( handleDelete(file.id)}> } > handleOpen(file)}> ))} ``` --- ## Utility Components ### ClickAwayListener Detects clicks outside the wrapped element — essential for custom dropdowns and popovers. ```tsx import ClickAwayListener from '@mui/material/ClickAwayListener'; {open && ( Option 1 Option 2 )} ``` ### Portal Renders children into `document.body` or a custom container. ```tsx import Portal from '@mui/material/Portal'; ``` ### NoSsr — Client-Only Rendering ```tsx import NoSsr from '@mui/material/NoSsr'; }> {/* Depends on window — fails on server */} ``` ### useMediaQuery — Responsive Conditional Rendering ```tsx 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 ? : ; } // 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: ```tsx import { Link as RouterLink } from 'react-router-dom'; // Button as router link // ListItemButton as router link // Typography as a label Email Address // Card as semantic article ... // Box as section with semantic HTML Title ``` --- ## Global Configuration via Theme defaultProps Set defaults for every instance of a component across the app: ```tsx 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 `` renders as contained by default — no need to repeat `variant="contained"` everywhere.