MUI - Responsive Drawer
For those who're working with material-UI V1 and higher, using breakpoints is the best way to make responsive layouts.
Breakpoints:
- xs, extra-small: 0px or larger
- sm, small: 600px or larger
- md, medium: 960px or larger
- lg, large: 1280px or larger
- xl, xlarge: 1920px or larger
So, for changing React rendering tree you should pass different breakpoint parameter value into breakpoints.up()
in the JSS:
navIconHide: { [theme.breakpoints.up('md')]: { display: 'none', }, }, toolbar: theme.mixins.toolbar, drawerPaper: { width: drawerWidth, [theme.breakpoints.up('md')]: { position: 'relative', }, },
The whole Drawer source code follows below.
import React from 'react';import PropTypes from 'prop-types';import { withStyles } from '@material-ui/core/styles';import Drawer from '@material-ui/core/Drawer';import AppBar from '@material-ui/core/AppBar';import Toolbar from '@material-ui/core/Toolbar';import List from '@material-ui/core/List';import Typography from '@material-ui/core/Typography';import IconButton from '@material-ui/core/IconButton';import Hidden from '@material-ui/core/Hidden';import Divider from '@material-ui/core/Divider';import MenuIcon from '@material-ui/icons/Menu';import { mailFolderListItems, otherMailFolderListItems } from './tileData';const drawerWidth = 240;const styles = theme => ({ root: { flexGrow: 1, height: 430, zIndex: 1, overflow: 'hidden', position: 'relative', display: 'flex', width: '100%', }, appBar: { position: 'absolute', marginLeft: drawerWidth, [theme.breakpoints.up('md')]: { width: `calc(100% - ${drawerWidth}px)`, }, }, navIconHide: { [theme.breakpoints.up('md')]: { display: 'none', }, }, toolbar: theme.mixins.toolbar, drawerPaper: { width: drawerWidth, [theme.breakpoints.up('md')]: { position: 'relative', }, }, content: { flexGrow: 1, backgroundColor: theme.palette.background.default, padding: theme.spacing.unit * 3, },});class ResponsiveDrawer extends React.Component { state = { mobileOpen: false, }; handleDrawerToggle = () => { this.setState(state => ({ mobileOpen: !state.mobileOpen })); }; render() { const { classes, theme } = this.props; const drawer = ( <div> <div className={classes.toolbar} /> <Divider /> <List>{mailFolderListItems}</List> <Divider /> <List>{otherMailFolderListItems}</List> </div> ); return ( <div className={classes.root}> <AppBar className={classes.appBar}> <Toolbar> <IconButton color="inherit" aria-label="Open drawer" onClick={this.handleDrawerToggle} className={classes.navIconHide} > <MenuIcon /> </IconButton> <Typography variant="title" color="inherit" noWrap> Responsive drawer </Typography> </Toolbar> </AppBar> <Hidden mdUp> <Drawer variant="temporary" anchor={theme.direction === 'rtl' ? 'right' : 'left'} open={this.state.mobileOpen} onClose={this.handleDrawerToggle} classes={{ paper: classes.drawerPaper, }} ModalProps={{ keepMounted: true, // Better open performance on mobile. }} > {drawer} </Drawer> </Hidden> <Hidden smDown implementation="css"> <Drawer variant="permanent" open classes={{ paper: classes.drawerPaper, }} > {drawer} </Drawer> </Hidden> <main className={classes.content}> <div className={classes.toolbar} /> <Typography noWrap>{'You think water moves fast? You should see ice.'}</Typography> </main> </div> ); }}ResponsiveDrawer.propTypes = { classes: PropTypes.object.isRequired, theme: PropTypes.object.isRequired,};export default withStyles(styles, { withTheme: true })(ResponsiveDrawer);
You could listen for screen size changes in componentWillMount like this, I'm sure there are better methods but this works.
toggleOpenDrawer = () => { if (!this.state.mobile) { return; } this.setState({ open: !this.state.open })}setSmall = () => { this.setState({open: false, docked: false, mobile: true})}setLarge = () => { this.setState({open: true, docked: true, mobile: false})}componentWillMount() { const mediaQuery = window.matchMedia('(min-width: 768px)'); if (mediaQuery.matches) { this.setLarge() } else { this.setSmall() } mediaQuery.addListener((mq) => { if (mq.matches) { this.setLarge() } else { this.setSmall() } });}
In MUI v5, you can display 2 Drawers
in different screen sizes. The permanent Drawer
is always visible in the larger screen. In small screen, you can remove it easily using responsive values in sx
prop:
<Drawer variant="permanent" open sx={{ display: { xs: 'none', sm: 'block' } }}>
And show a temporary Drawer
that can be opened or closed. The temporary one doesn't affect the layout since it appears on top which is suitable in mobile view.
<Drawer variant="temporary" open={mobileOpen} onClose={handleDrawerToggle} sx={{ display: { xs: 'block', sm: 'none' } }}>