React Material UI - herougo/SoftwareEngineerKnowledgeRepository GitHub Wiki
Sources:
- https://mui.com/material-ui/getting-started/
- Codevolution: https://www.youtube.com/playlist?list=PLC3y8-rFHvwh-K9mDlrrcDywl7CeVL2rO
How do you add css styles to material UI components?
- answer: sx prop
https://mui.com/material-ui/getting-started/installation/
Please note that react and react-dom are peer dependencies, meaning you should ensure they are installed before installing Material UI.
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0"
},
npm install @mui/material @emotion/react @emotion/styled
for text
import Typography from '@mui/material/Typography';
// body1 is the default variant
<Typography variant='h1'>My h1 heading text</Typography>
<Typography variant='h4' gutterBottom>My h4 heading text with bottom margin</Typography>
<Typography variant='subtitle1'>My subtitle1 text</Typography>
<Typography variant='subtitle2'>My subtitle2 text</Typography>
<Typography>My body1 text</Typography>
<Typography variant='body2'>My body2 text</Typography>import { Stack, Button, IconButton } from '@mui/material';
import SendIcon from '@mui/icons-material/Send';
<Stack spacing={2} direction='column'>
<Button variant='text' onClick={() => alert('Clicked')}>Text</Button>
<Button variant='contained'>Coloured button with white text</Button>
<Button variant='outlined' color='primary' size='medium'>White button with coloured border</Button>
<Button startIcon={<SendIcon />}>Send</Button>
<Button endIcon={<SendIcon />}>Send</Button>
<Button endIcon={<SendIcon />}>Send</Button>
<IconButton aria-label='send'>
<sendIcon />
</IconButton>
</Stack>
// color can be primary (default), secondary, error warning, info, success, etc
// size can be small, medium (default), large
// startIcon puts icon before text, endIcon puts icon after text
// aria-label for accessibilityimport { Stack, Button, ButtonGroup } from '@mui/material';
<ButtonGroup
variant='contained'
orientation='vertical'
size='small'
color='secondary'
aria-label='alignment button group'
>
<Button startIcon={<SendIcon />}>Send Top</Button>
<Button endIcon={<SendIcon />}>Send Middle</Button>
<Button endIcon={<SendIcon />}>Send Bottom</Button>
</ButtonGroup>
// buttons form a group where the only rounded corners are the top of the
// top bottom and the bottom of the bottom button.import { Stack, ToggleButton, ToggleButtonGroup } from '@mui/material';
import FormatBoldIcon from '@mui/icons-material/FormatBold';
import FormatItalicIcon from '@mui/icons-material/FormatItalic';
export const MuiButton = () => {
const [formats, setFormats] = useState([])
const handleFormatChange = (_event, updatedFormats) => { setFormats(updatedFormats) }
return (
<ToggleButtonGroup
aria-label='text formatting'
value={formats}
onChange={handleFormatChange}
color='secondary'
size='medium'
>
<ToggleButton value='bold' aria-label='bold'><FormatBoldIcon /></ToggleButton>
<ToggleButton value='italic' aria-label='italic'><FormatItalicIcon /></ToggleButton>
</ToggleButtonGroup>
)
}Input text with label embedded in the text field (moves to the top when text is inputted).
See for what label does: https://mui.com/material-ui/react-text-field/
import { TextField, InputAdornment } from '@mui/material'
// InputAdorment is for prefixes/suffixes
<TextField
label='Name'
required
helperText='I am text below the text field'
value={value}
onChange={e => setValue(e.target.value)}
error={!value}
/>
<TextField label='Amount' InputProps={{
startAdornment: <InputAdornment>$</InputAdornment>
}} />
<TextField label='Weight' InputProps={{
endAdornment: <InputAdornment>kg</InputAdornment>
}} />Select
import { TextField, MenuItem } from '@mui/material'
export const MuiButton = () => {
const [countries, setCountries] = useState([])
const handleFormatChange = (e) => {
const value = e.target.value;
setCountries(typeof value === 'string' ? value.split(',') : value);
}
// InputAdorment is for prefixes/suffixes
<TextField
label='Select Country'
select
value={country}
onChange={handleChange}
fullWidth
SelectProps={{multiple:true}}
>
<MenuItem value='IN'>India</MenuItem>
<MenuItem value='US'>USA</MenuItem>
<MenuItem value='AU'>Australia</MenuItem>
</TextField>
// fullWidth expands select width to parentimport { useState } from 'react'
import {
Box,
FormControl,
FormLabel,
FormControlLabel,
RadioGroup,
Radio
} from '@mui/material'
const MuiRadioButton = () => {
const [value, setValue] = setState('')
const handleChange = (event) => {
setValue(event.target.value)
}
return (
<Box>
<FormControl>
<FormLabel id='job-experience-group-label'>
Years of Experience
</FormLabel>
<RadioGroup
name='job-experience-group'
aria-labelledby='job-experience-group-label'
value={value}
onChange={handleChange}
>
<FormControlLabel control={<Radio />} label='0-2' value='0-2' />
<FormControlLabel control={<Radio />} label='3-5' value='3-5' />
<FormControlLabel control={<Radio />} label='6-10' value='6-10' />
</RadioGroup>
</FormControl>
</Box>
)
}import { useState } from 'react'
import {
Box,
FormControlLabel,
Checkbox,
FormControl,
FormLabel,
FormGroup
} from '@mui/material'
import BookmarkBorderIcon from '@mui/icons-material/BookmarkBorder'
import BookmarkIcon from '@mui/icons-material/Bookmark'
export const MuiCheckbox = () => {
const [acceptTnC, setAcceptTnC] = useState(false)
const [skills, setSkills] = useState<string[]>([])
console.log(skills)
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setAcceptTnC(event.target.checked)
}
const handleSkillChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const index = skills.indexOf(event.target.value)
if (index === -1) {
setSkills([...skills, event.target.value])
} else {
setSkills(skills.filter(skill => skill !== event.target.value))
}
}
return (
<Box>
<Box>
<FormControlLabel
control={
<Checkbox
checked={acceptTnC}
onChange={handleChange}
size='small'
color='secondary'
/>
}
label='Accept terms and conditions'
/>
</Box>
<Box>
<Checkbox
icon={<BookmarkBorderIcon />}
checkedIcon={<BookmarkIcon />}
checked={acceptTnC}
onChange={handleChange}
/>
</Box>
<Box>
<FormControl error>
<FormLabel>Skills</FormLabel>
<FormGroup>
<FormControlLabel
control={
<Checkbox
value='html'
checked={skills.includes('html')}
onChange={handleSkillChange}
/>
}
label='HTML'
/>
<FormControlLabel
control={
<Checkbox
value='css'
checked={skills.includes('css')}
onChange={handleSkillChange}
/>
}
label='CSS'
/>
<FormControlLabel
control={
<Checkbox
value='javascript'
checked={skills.includes('javascript')}
onChange={handleSkillChange}
/>
}
label='JavaScript'
/>
</FormGroup>
</FormControl>
</Box>
</Box>
)
}import { Box, FormControlLabel, Switch } from '@mui/material'
import { useState } from 'react'
export const MuiSwitch = () => {
const [checked, setChecked] = useState(false)
console.log(checked)
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setChecked(event.target.checked)
}
return (
<Box>
<FormControlLabel
control={<Switch checked={checked} onChange={handleChange} />}
label='Dark mode'
/>
</Box>
)
}import { Stack, Rating } from '@mui/material'
import { useState } from 'react'
import FavoriteIcon from '@mui/icons-material/Favorite'
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder'
export const MuiRating = () => {
const [value, setValue] = useState<number | null>(3)
console.log(value)
const handleChange = (
_event: React.ChangeEvent<{}>,
newValue: number | null
) => {
setValue(newValue)
}
return (
<Stack spacing={2}>
<Rating
value={value}
onChange={handleChange}
precision={0.5}
size='large'
icon={<FavoriteIcon fontSize='inherit' color='error' />}
emptyIcon={<FavoriteBorderIcon fontSize='inherit' />}
readOnly
/>
</Stack>
)
}import { Stack, Autocomplete, TextField } from '@mui/material'
import { useState } from 'react'
type Skill = {
id: number
label: string
}
const skills = ['HTML', 'CSS', 'JavaScript', 'TypeScript', 'React']
const skillsOptions = skills.map((skill, index) => ({
id: index + 1,
label: skill
}))
export const MuiAutocomplete = () => {
const [value, setValue] = useState<string | null>(null)
const [skill, setSkill] = useState<Skill | null>(null)
console.log(skill)
return (
<Stack spacing={2} width='250px'>
<Autocomplete
options={skills}
renderInput={params => <TextField {...params} label='Skills' />}
value={value}
onChange={(event: any, newValue: string | null) => {
setValue(newValue)
}}
/>
<Autocomplete
options={skillsOptions}
renderInput={params => <TextField {...params} label='Skills' />}
value={skill}
onChange={(_event: any, newValue: Skill | null) => {
setSkill(newValue)
}}
/>
</Stack>
)
}The Paper component is a container for displaying content on an elevated surface.
The Box component is a generic container for grouping other components. It's a fundamental building block when working with Material UI—you can think of it as a
Stack is a container component for arranging elements vertically or horizontally.
import { Box, Stack, Grid, Paper } from '@mui/material'
export const MuiLayout = () => {
return (
<Paper sx={{ padding: '32px' }} elevation={2}>
<Stack border='1px solid' spacing={2} direction='row'>
<Box
component='span'
sx={{
backgroundColor: 'primary.main',
color: 'white',
height: '100px',
width: '100px',
padding: '16px',
'&:hover': {
backgroundColor: 'primary.light'
}
}}>
Codevolution
</Box>
<Box
display='flex'
height='100px'
width='100px'
bgcolor='success.light'
p={2}></Box>
</Stack>
<Grid rowSpacing={2} columnSpacing={1} container my={4}>
<Grid item xs={6}>
<Box p={2} bgcolor='primary.light'>
Item 1
</Box>
</Grid>
<Grid item xs={6}>
<Box p={2} bgcolor='primary.light'>
Item 2
</Box>
</Grid>
<Grid item xs={6}>
<Box p={2} bgcolor='primary.light'>
Item 3
</Box>
</Grid>
<Grid item xs={6}>
<Box p={2} bgcolor='primary.light'>
Item 4
</Box>
</Grid>
</Grid>
</Paper>
)
}import {
Box,
Card,
CardContent,
CardActions,
Typography,
Button,
CardMedia
} from '@mui/material'
export const MuiCard = () => {
return (
<Box width='300px'>
<Card>
<CardMedia
component='img'
height='140'
image='https://source.unsplash.com/random'
alt='unsplash image'
/>
<CardContent>
<Typography gutterBottom variant='h5' component='div'>
React
</Typography>
<Typography variant='body2' color='text.secondary'>
React is a JavaScript library for building user interfaces. React
can be used as a base in the development of single-page or mobile
applications.
</Typography>
</CardContent>
<CardActions>
<Button size='small'>Share</Button>
<Button size='small'>Learn More</Button>
</CardActions>
</Card>
</Box>
)
}Accordion = Collapsible thing
import {
Accordion,
AccordionSummary,
AccordionDetails,
Typography
} from '@mui/material'
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
import { useState } from 'react'
export const MuiAccordion = () => {
const [expanded, setExpanded] = useState<string | false>(false)
const handleChange = (isExpanded: boolean, panel: string) => {
setExpanded(isExpanded ? panel : false)
}
return (
<>
<Accordion
expanded={expanded === 'panel1'}
onChange={(event, isExpanded) => handleChange(isExpanded, 'panel1')}>
<AccordionSummary
aria-controls='panel1-content'
id='panel1-header'
expandIcon={<ExpandMoreIcon />}>
<Typography>Accordion 1</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse
malesuada lacus ex, sit amet blandit leo lobortis eget.
</Typography>
</AccordionDetails>
</Accordion>
<Accordion
expanded={expanded === 'panel2'}
onChange={(event, isExpanded) => handleChange(isExpanded, 'panel2')}>
<AccordionSummary
aria-controls='panel2-content'
id='panel2-header'
expandIcon={<ExpandMoreIcon />}>
<Typography>Accordion 2</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse
malesuada lacus ex, sit amet blandit leo lobortis eget.
</Typography>
</AccordionDetails>
</Accordion>
</>
)
}(lazy loading means loaded as needed (i.e. in view))
import { Stack, ImageList, ImageListItem, Box } from '@mui/material'
export const MuiImageList = () => {
return (
<Stack spacing={4}>
<ImageList sx={{ width: 500, height: 450 }} cols={3} rowHeight={164}>
{itemData.map(item => (
<ImageListItem key={item.img}>
<img
src={`${item.img}?w=164&h=164&fit=crop&auto=format&dpr=2`}
alt={item.title}
loading='lazy'
/>
</ImageListItem>
))}
</ImageList>
<ImageList
sx={{ width: 500, height: 450 }}
variant='woven'
cols={3}
gap={8}>
{itemData2.map(item => (
<ImageListItem key={item.img}>
<img
src={`${item.img}?w=164&h=164&fit=crop&auto=format&dpr=2`}
alt={item.title}
loading='lazy'
/>
</ImageListItem>
))}
</ImageList>
<Box sx={{ width: 500, height: 450, overflowY: 'scroll' }}>
<ImageList variant='masonry' cols={3} gap={8}>
{itemData3.map(item => (
<ImageListItem key={item.img}>
<img
src={`${item.img}?w=248&fit=crop&auto=format&dpr=2`}
alt={item.title}
loading='lazy'
/>
</ImageListItem>
))}
</ImageList>
</Box>
</Stack>
)
}
const itemData = [
{
img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
title: 'Breakfast'
},
...
]Menu - popup display of options next to something
import {
AppBar,
Toolbar,
IconButton,
Typography,
Button,
Stack,
Menu,
MenuItem
} from '@mui/material'
import CatchingPokemonIcon from '@mui/icons-material/CatchingPokemon'
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'
import { useState } from 'react'
export const MuiNavbar = () => {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
const open = Boolean(anchorEl)
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget)
}
const handleClose = () => {
setAnchorEl(null)
}
return (
<AppBar position='static' color='transparent'>
<Toolbar>
<IconButton size='large' edge='start' color='inherit' aria-label='logo'>
<CatchingPokemonIcon />
</IconButton>
<Typography variant='h6' component='div' sx={{ flexGrow: 1 }}>
POKEMONAPP
</Typography>
<Stack direction='row' spacing={2}>
<Button color='inherit'>Features</Button>
<Button color='inherit'>Pricing</Button>
<Button color='inherit'>About</Button>
<Button
color='inherit'
id='resources-button'
aria-controls={open ? 'resources-menu' : undefined}
aria-haspopup='true'
aria-expanded={open ? 'true' : undefined}
endIcon={<KeyboardArrowDownIcon />}
onClick={handleClick}>
Resources
</Button>
<Button color='inherit'>Login</Button>
</Stack>
<Menu
id='resources-menu'
anchorEl={anchorEl}
open={open}
onClose={handleClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right'
}}
MenuListProps={{
'aria-labelledby': 'resources-button'
}}>
<MenuItem onClick={handleClose}>Blog</MenuItem>
<MenuItem onClick={handleClose}>Podcast</MenuItem>
</Menu>
</Toolbar>
</AppBar>
)
}import { Stack, Link, Typography } from '@mui/material'
export const MuiLink = () => {
return (
<Stack spacing={2} m={4} direction='row'>
<Typography variant='h6'>
<Link href='#'>Link</Link>
</Typography>
<Link href='#' color='secondary' underline='hover'>
Secondary
</Link>
<Link href='#' variant='body2' underline='none'>
Body 2 link
</Link>
</Stack>
)
}When displaying a path with each section of the path clickable.
Example:
Home / Catalog / Accessories / New Collection
import { Box, Breadcrumbs, Link, Typography } from '@mui/material'
import NavigateNextIcon from '@mui/icons-material/NavigateNext'
export const MuiBreadcrumbs = () => {
return (
<Box m={2}>
<Breadcrumbs
separator={<NavigateNextIcon fontSize='small' />}
maxItems={3}
itemsAfterCollapse={2}
aria-label='breadcrumb'>
<Link underline='hover' href='#'>
Home
</Link>
<Link underline='hover' href='#'>
Catalog
</Link>
<Link underline='hover' href='#'>
Accessories
</Link>
<Link underline='hover' href='#'>
New Collection
</Link>
<Typography color='text.primary'>Shoes</Typography>
</Breadcrumbs>
</Box>
)
}i.e. sliding section from hamburger buttons
import { Drawer, Box, Typography, IconButton } from '@mui/material'
import { useState } from 'react'
import MenuIcon from '@mui/icons-material/Menu'
export const MuiDrawer = () => {
const [isDrawerOpen, setIsDrawerOpen] = useState(false)
return (
<>
<IconButton
onClick={() => setIsDrawerOpen(true)}
size='large'
edge='start'
color='inherit'
aria-label='logo'>
<MenuIcon />
</IconButton>
<Drawer
anchor='left'
open={isDrawerOpen}
onClose={() => setIsDrawerOpen(false)}>
<Box p={2} width='250px' role='presentation' textAlign='center'>
<Typography variant='h6' component='div'>
Side Panel
</Typography>
</Box>
</Drawer>
</>
)
}i.e. + circle button which expands to other icon buttons (usually used on mobile)
import { SpeedDial, SpeedDialAction, SpeedDialIcon } from '@mui/material'
import CopyIcon from '@mui/icons-material/FileCopyOutlined'
import PrintIcon from '@mui/icons-material/Print'
import ShareIcon from '@mui/icons-material/Share'
import EditIcon from '@mui/icons-material/Edit'
export const MuiSpeedDial = () => {
return (
<SpeedDial
ariaLabel='Navigation speed dial'
sx={{ position: 'absolute', bottom: 16, right: 16 }}
icon={<SpeedDialIcon openIcon={<EditIcon />} />}>
<SpeedDialAction icon={<CopyIcon />} tooltipTitle='Copy' tooltipOpen />
<SpeedDialAction icon={<PrintIcon />} tooltipTitle='Print' tooltipOpen />
<SpeedDialAction icon={<ShareIcon />} tooltipTitle='Share' tooltipOpen />
</SpeedDial>
)
}used more for mobile
import { BottomNavigation, BottomNavigationAction } from '@mui/material'
import HomeIcon from '@mui/icons-material/Home'
import FavoriteIcon from '@mui/icons-material/Favorite'
import PersonIcon from '@mui/icons-material/Person'
import { useState } from 'react'
export const MuiBottomNavigation = () => {
const [value, setValue] = useState(0)
return (
<BottomNavigation
sx={{ width: '100%', position: 'absolute', bottom: 0 }}
showLabels
value={value}
onChange={(event, newValue) => {
setValue(newValue)
}}>
<BottomNavigationAction label='Home' icon={<HomeIcon />} />
<BottomNavigationAction label='Favorites' icon={<FavoriteIcon />} />
<BottomNavigationAction label='Profile' icon={<PersonIcon />} />
</BottomNavigation>
)
}circular profile pictures
import { Stack, Avatar, AvatarGroup } from '@mui/material'
export const MuiAvatar = () => {
return (
<Stack spacing={4}>
<Stack direction='row' spacing={1}>
<Avatar sx={{ bgcolor: 'primary.light' }}>BW</Avatar>
<Avatar sx={{ bgcolor: 'success.light' }}>CK</Avatar>
</Stack>
<Stack direction='row' spacing={1}>
<AvatarGroup max={3}>
<Avatar sx={{ bgcolor: 'primary.light' }}>BW</Avatar>
<Avatar sx={{ bgcolor: 'success.light' }}>CK</Avatar>
<Avatar
src='https://randomuser.me/api/portraits/women/79.jpg'
alt='Jane Doe'
/>
<Avatar
src='https://randomuser.me/api/portraits/men/51.jpg'
alt='John Doe'
/>
</AvatarGroup>
</Stack>
<Stack direction='row' spacing={1}>
<Avatar
variant='rounded'
sx={{ bgcolor: 'primary.light', width: 48, height: 48 }}>
BW
</Avatar>
<Avatar
variant='rounded'
sx={{ bgcolor: 'success.light', width: 48, height: 48 }}>
CK
</Avatar>
</Stack>
</Stack>
)
}e.g. showing how many notifications you got (circle on top right of icon)
import { Stack, Badge } from '@mui/material'
import MailIcon from '@mui/icons-material/Mail'
export const MuiBadge = () => {
return (
<Stack spacing={2} direction='row'>
<Badge badgeContent={5} color='secondary'>
<MailIcon />
</Badge>
<Badge badgeContent={0} color='secondary' showZero>
<MailIcon />
</Badge>
<Badge badgeContent={100} color='secondary' max={999}>
<MailIcon />
</Badge>
<Badge color='secondary' variant='dot'>
<MailIcon />
</Badge>
</Stack>
)
}e.g. Email List
import {
Box,
List,
ListItem,
ListItemText,
ListItemButton,
ListItemIcon,
Divider,
ListItemAvatar,
Avatar
} from '@mui/material'
import InboxIcon from '@mui/icons-material/Inbox'
export const MuiList = () => {
return (
<Box sx={{ width: '400px', bgcolor: '#efefef' }}>
<List>
<ListItem disablePadding>
<ListItemButton>
<ListItemIcon>
<InboxIcon />
</ListItemIcon>
<ListItemText primary='List item 1' />
</ListItemButton>
</ListItem>
<Divider />
<ListItem>
<ListItemAvatar>
<Avatar>
<InboxIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary='List item 2' secondary='Secondary text' />
</ListItem>
<ListItem>
<ListItemText primary='List item 3' />
</ListItem>
</List>
</Box>
)
}Pill-shaped things which can be used for (possibly closeable) tags
import { Stack, Chip, Avatar } from '@mui/material'
import { useState } from 'react'
export const MuiChip = () => {
const [chips, setChips] = useState(['Chip 1', 'Chip 2', 'Chip 3'])
const handleDelete = (chipToDelete: string) => {
setChips(chips => chips.filter(chip => chip !== chipToDelete))
}
return (
<Stack direction='row' spacing={1}>
<Chip label='Chip' color='primary' size='small' />
<Chip
label='Chip Outlined'
variant='outlined'
color='secondary'
avatar={<Avatar>V</Avatar>}
/>
<Chip label='Click' color='success' onClick={() => alert('Clicked')} />
<Chip
label='Delete'
color='error'
onClick={() => alert('Clicked')}
onDelete={() => alert('Delete')}
/>
{chips.map(chip => (
<Chip key={chip} label={chip} onDelete={() => handleDelete(chip)} />
))}
</Stack>
)
}For example, when you hover over an edit icon, the "Edit" text shows up to describe it.
import { Tooltip, IconButton } from '@mui/material'
import DeleteIcon from '@mui/icons-material/Delete'
export const MuiTooltip = () => {
return (
<Tooltip
title='Delete'
placement='right'
arrow
enterDelay={500}
leaveDelay={200}>
<IconButton>
<DeleteIcon />
</IconButton>
</Tooltip>
)
}import {
TableContainer,
Table,
TableHead,
TableBody,
TableRow,
TableCell,
Paper
} from '@mui/material'
export const MuiTable = () => {
return (
<TableContainer sx={{ maxHeight: '300px' }} component={Paper}>
<Table stickyHeader aria-label='simple table'>
<TableHead>
<TableRow>
<TableCell>Id</TableCell>
<TableCell>First Name</TableCell>
<TableCell>Last Name</TableCell>
<TableCell align='center'>Email</TableCell>
</TableRow>
</TableHead>
<TableBody>
{tableData.map(row => (
<TableRow
key={row.id}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
<TableCell>{row.id}</TableCell>
<TableCell>{row.first_name}</TableCell>
<TableCell>{row.last_name}</TableCell>
<TableCell align='center'>{row.email}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)
}
const tableData = [
{
id: 1,
first_name: 'Beret',
last_name: 'Lennard',
email: '[email protected]',
gender: 'Female',
ip_address: '213.196.192.52'
},
...
]import { Stack, Alert, AlertTitle, Button } from '@mui/material'
import CheckIcon from '@mui/icons-material/Check'
export const MuiAlert = () => {
return (
<Stack spacing={2}>
<Alert severity='error' onClose={() => alert('Close alert')}>
<AlertTitle>Error</AlertTitle>This is an error alert
</Alert>
<Alert severity='warning'>
<AlertTitle>Warning</AlertTitle>This is a warning alert
</Alert>
<Alert severity='info'>
<AlertTitle>Info</AlertTitle>This is an info alert
</Alert>
<Alert
severity='success'
icon={<CheckIcon fontSize='inherit' />}
action={
<Button color='inherit' size='small'>
UNDO
</Button>
}>
<AlertTitle>Success</AlertTitle>This is a success alert
</Alert>
<Alert variant='outlined' severity='error'>
This is an outlined error alert
</Alert>
<Alert variant='filled' severity='error'>
This is an filled error alert
</Alert>
</Stack>
)
}e.g. when you send an email and a popup shows up saying the email is sent
import { Snackbar, Button, Alert, AlertProps } from '@mui/material'
import { useState, forwardRef } from 'react'
const SnackbarAlert = forwardRef<HTMLDivElement, AlertProps>(
function SnackbarAlert(props, ref) {
return <Alert elevation={6} ref={ref} {...props} />
}
)
export const MuiSnackbar = () => {
const [open, setOpen] = useState(false)
const handleClose = (
event?: React.SyntheticEvent | Event,
reason?: string
) => {
if (reason === 'clickaway') {
return
}
setOpen(false)
}
return (
<>
<Button onClick={() => setOpen(true)}>Submit</Button>
<Snackbar
open={open}
autoHideDuration={4000}
onClose={handleClose}
message='Form submitted successfully!'
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
/>
<Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
<SnackbarAlert onClose={handleClose} severity='success'>
Form submitted successfully!
</SnackbarAlert>
</Snackbar>
</>
)
}i.e. popup / modal
import {
Button,
Dialog,
DialogTitle,
DialogContent,
DialogContentText,
DialogActions
} from '@mui/material'
import { useState } from 'react'
export const MuiDialog = () => {
const [open, setOpen] = useState(false)
return (
<>
<Button onClick={() => setOpen(true)}>Open dialog</Button>
<Dialog
open={open}
onClose={() => setOpen(false)}
aria-labelledby='dialog-title'
aria-describedby='dialog-description'>
<DialogTitle id='dialog-title'>Submit the test?</DialogTitle>
<DialogContent>
<DialogContentText id='dialog-description'>
Are you sure you want to submit the test? You will not be able to
edit it after submitting.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => setOpen(false)}>Cancel</Button>
<Button onClick={() => setOpen(false)} autoFocus>
Submit
</Button>
</DialogActions>
</Dialog>
</>
)
}import { Stack, CircularProgress, LinearProgress } from '@mui/material'
export const MuiProgress = () => {
return (
<Stack spacing={2}>
<CircularProgress />
<CircularProgress color='success' />
<CircularProgress variant='determinate' value={60} />
<LinearProgress />
<LinearProgress color='success' />
<LinearProgress variant='determinate' value={60} />
</Stack>
)
}Flashing grey background as substitute when things are loading
import { Avatar, Box, Skeleton, Stack, Typography } from '@mui/material'
import { useState, useEffect } from 'react'
export const MuiSkeleton = () => {
const [loading, setLoading] = useState(true)
useEffect(() => {
setTimeout(() => {
setLoading(false)
}, 3000)
}, [])
return (
<Box sx={{ width: '250px' }}>
{loading ? (
<Skeleton
variant='rectangular'
width={256}
height={144}
animation='wave'
/>
) : (
<img
src='https://source.unsplash.com/random/256x144'
alt='skeleton'
width={256}
height={144}
/>
)}
<Stack
direction='row'
spacing={1}
sx={{ width: '100%', marginTop: '12px' }}>
{loading ? (
<Skeleton
variant='circular'
width={40}
height={40}
animation='wave'
/>
) : (
<Avatar>V</Avatar>
)}
<Stack sx={{ width: '80%' }}>
{loading ? (
<>
<Typography variant='body1'>
<Skeleton variant='text' width='100%' animation='wave' />
</Typography>
<Typography variant='body2'>
<Skeleton variant='text' width='100%' animation='wave' />
</Typography>
</>
) : (
<>
<Typography variant='body1'>React MUI Tutorial</Typography>
</>
)}
</Stack>
</Stack>
</Box>
)
}useful for submit buttons which you don't want to click twice
import { Stack } from '@mui/material'
import { LoadingButton } from '@mui/lab'
import SaveIcon from '@mui/icons-material/Save'
export const MuiLoadingButton = () => {
return (
<Stack direction='row' spacing={2}>
<LoadingButton variant='outlined'>Submit</LoadingButton>
<LoadingButton loading variant='outlined'>
Submit
</LoadingButton>
<LoadingButton loadingIndicator='Loading...' variant='outlined'>
Fetch data
</LoadingButton>
<LoadingButton loading loadingIndicator='Loading...' variant='outlined'>
Fetch data
</LoadingButton>
<LoadingButton
loadingPosition='start'
startIcon={<SaveIcon />}
variant='outlined'>
Save
</LoadingButton>
<LoadingButton
loading
loadingPosition='start'
startIcon={<SaveIcon />}
variant='outlined'>
Save
</LoadingButton>
</Stack>
)
}import { Stack, TextField } from '@mui/material'
import { DatePicker, TimePicker, DateTimePicker } from '@mui/lab'
import { useState } from 'react'
export const MuiDateTimePicker = () => {
const [selectedDate, setSelectedDate] = useState<Date | null>(null)
const [selectedTime, setSelectedTime] = useState<Date | null>(null)
const [selectedDateTime, setSelectedDateTime] = useState<Date | null>(null)
console.log({
selectedDate,
selectedTime: selectedTime && selectedTime.toLocaleTimeString(),
selectedDateTime
})
return (
<Stack spacing={4} sx={{ width: '250px' }}>
<DatePicker
label='Date Picker'
value={selectedDate}
onChange={newValue => {
setSelectedDate(newValue)
}}
renderInput={params => <TextField {...params} />}
/>
<TimePicker
label='Time Picker'
value={selectedTime}
onChange={newValue => {
setSelectedTime(newValue)
}}
renderInput={params => <TextField {...params} />}
/>
<DateTimePicker
label='Date Time Picker'
value={selectedDateTime}
onChange={newValue => {
setSelectedDateTime(newValue)
}}
renderInput={params => <TextField {...params} />}
/>
</Stack>
)
}import { Box, TextField } from '@mui/material'
import { DateRangePicker, DateRange } from '@mui/lab'
import { useState } from 'react'
export const MuiDateRangePicker = () => {
const [value, setValue] = useState<DateRange<Date>>([null, null])
console.log('value', value)
return (
<Box width='500px'>
<DateRangePicker
startText='Check-in'
endText='Check-out'
value={value}
onChange={newValue => {
setValue(newValue)
}}
renderInput={(startProps, endProps) => (
<>
<TextField {...startProps} />
<Box sx={{ mx: 2 }}> to </Box>
<TextField {...endProps} />
</>
)}
/>
</Box>
)
}import { Box, Tab } from '@mui/material'
import { TabContext, TabList, TabPanel } from '@mui/lab'
import { useState } from 'react'
import FavoriteIcon from '@mui/icons-material/Favorite'
export const MuiTabs = () => {
const [value, setValue] = useState('1')
const handleChange = (event: React.SyntheticEvent, newValue: string) => {
setValue(newValue)
}
return (
<Box sx={{ width: '300px' }}>
<TabContext value={value}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<TabList
onChange={handleChange}
aria-label='Tabs example'
textColor='secondary'
indicatorColor='secondary'
centered
variant='scrollable'
scrollButtons='auto'>
<Tab
icon={<FavoriteIcon />}
iconPosition='start'
label='Tab One'
value='1'
/>
<Tab label='Tab Two' value='2' disabled />
<Tab label='Tab Three' value='3' />
<Tab label='Tab Four' value='4' />
<Tab label='Tab Five' value='5' />
<Tab label='Tab Six' value='6' />
</TabList>
</Box>
<TabPanel value='1'>Item One</TabPanel>
<TabPanel value='2'>Item Two</TabPanel>
<TabPanel value='3'>Item Three</TabPanel>
<TabPanel value='4'>Item Four</TabPanel>
<TabPanel value='5'>Item Five</TabPanel>
<TabPanel value='6'>Item Six</TabPanel>
</TabContext>
</Box>
)
}import {
Timeline,
TimelineItem,
TimelineSeparator,
TimelineConnector,
TimelineContent,
TimelineDot,
TimelineOppositeContent
} from '@mui/lab'
export const MuiTimeline = () => {
return (
<Timeline>
<TimelineItem>
<TimelineOppositeContent color='text.secondary'>
09:30 am
</TimelineOppositeContent>
<TimelineSeparator>
<TimelineDot color='secondary' variant='outlined' />
<TimelineConnector />
</TimelineSeparator>
<TimelineContent>Eat</TimelineContent>
</TimelineItem>
<TimelineItem>
<TimelineOppositeContent color='text.secondary'>
10:30 am
</TimelineOppositeContent>
<TimelineSeparator>
<TimelineDot color='secondary' variant='outlined' />
<TimelineConnector />
</TimelineSeparator>
<TimelineContent>Code</TimelineContent>
</TimelineItem>
<TimelineItem>
<TimelineOppositeContent color='text.secondary'>
10:30 am
</TimelineOppositeContent>
<TimelineSeparator>
<TimelineDot color='secondary' variant='outlined' />
<TimelineConnector />
</TimelineSeparator>
<TimelineContent>Sleep</TimelineContent>
</TimelineItem>
<TimelineItem>
<TimelineOppositeContent color='text.secondary'>
09:00 am
</TimelineOppositeContent>
<TimelineSeparator>
<TimelineDot color='secondary' variant='outlined' />
</TimelineSeparator>
<TimelineContent>Repeat</TimelineContent>
</TimelineItem>
</Timeline>
)
}
display of N rectangular elements (with variable heights) in fixed length columns
import { Masonry } from '@mui/lab'
import {
Box,
Paper,
Accordion,
AccordionSummary,
AccordionDetails,
Typography
} from '@mui/material'
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
const heights = [
150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80
]
export const MuiMasonry = () => {
return (
<Box sx={{ width: 500, minHeight: 400 }}>
<Masonry columns={4} spacing={1}>
{heights.map((height, index) => (
<Paper
key={index}
sx={{
// display: 'flex',
// justifyContent: 'center',
// alignItems: 'center',
// height,
border: '1px solid'
}}>
<Accordion sx={{ minHeight: height }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>Accordion {index + 1}</Typography>
</AccordionSummary>
<AccordionDetails>Contents</AccordionDetails>
</Accordion>
</Paper>
))}
</Masonry>
</Box>
)
}<Box
sx={{
width: {
xs: 100, // 0 px
sm: 200, // 600 px
md: 300, // 900 px
lg: 400, // 1200 px
xl: 500, // 1536 px
}
}} />import { createTheme } from '@mui/material'
const theme = createTheme({
palette: {
secondary: {
main: colors.orange[500]
}
}
})
function App() {
return (
<ThemeProvider theme={theme}>
<div className='App'>
...
</div>
</ThemeProvider>
)
}