--- name: layout-responsive description: MUI layout components and responsive design patterns triggers: - layout - Grid - Stack - responsive - breakpoints - Container - useMediaQuery allowed-tools: - Read - Glob - Grep - Write - Edit globs: - "*.tsx" - "*.jsx" --- # MUI Layout and Responsive Design ## Grid v2 MUI v6 ships Grid v2 as default (imported from `@mui/material/Grid`). The `size` prop replaces the old `xs`/`sm`/`md` props. Grid v2 always uses CSS grid internally and no longer requires the `item` prop — every direct child of a `container` is a grid item. ### Basic grid ```tsx import Grid from '@mui/material/Grid'; import Paper from '@mui/material/Paper'; Full width header Main content (full on mobile, 8/12 on desktop) Sidebar (full on mobile, 4/12 on desktop) ``` ### size values ```tsx // Fixed column span // always 6/12 // Responsive spans // 'auto' — shrinks to content width // 'grow' — fills remaining space (equivalent to old xs="true") ``` ### Spacing and column/row gap ```tsx // Uniform spacing // Separate column and row spacing // Responsive spacing ``` ### Offset ```tsx // Offset pushes the item right by n columns Centered 4-column block // Responsive offset Centered on desktop, left-aligned on mobile ``` ### Nested grid ```tsx {/* Nested grid — no additional container needed in v2 */} Nested A Nested B Sidebar ``` --- ## Stack `Stack` is a one-dimensional layout component (flexbox row or column). Simpler than Grid for linear sequences of components. ### Basic usage ```tsx import Stack from '@mui/material/Stack'; import Divider from '@mui/material/Divider'; // Vertical stack (default direction) // Horizontal row {user.name} ``` ### Responsive direction ```tsx ``` ### Divider between items ```tsx } > Section A Section B Section C ``` ### useFlexGap By default Stack uses negative margin to simulate gaps. Set `useFlexGap` to use the CSS `gap` property instead — required when children have `overflow: hidden` or when the container has `overflow: hidden`. ```tsx {tags.map((tag) => )} ``` --- ## Container Centers content horizontally with a max-width. The main layout wrapper for page content. ```tsx import Container from '@mui/material/Container'; // Responsive max-width (uses theme breakpoints) {/* lg = 1200px by default */} Page title // Exact pixel constraint {/* sm = 600px */} // Disable max-width (full fluid width) // 'fixed' — jumps between fixed widths at each breakpoint // Typical page layout {/* ... */} {children} {/* footer content */} ``` --- ## Box as a Layout Primitive `Box` renders a `div` by default but accepts a `component` prop. It has full access to the `sx` prop and system shorthands. ```tsx import Box from '@mui/material/Box'; // Flex centering helper // Section spacing {children} // Scroll container {longList} ``` --- ## Breakpoints MUI's default breakpoints (in `px`): | Key | Min width | |-----|-----------| | xs | 0 | | sm | 600 | | md | 900 | | lg | 1200 | | xl | 1536 | ### useMediaQuery ```tsx import useMediaQuery from '@mui/material/useMediaQuery'; import { useTheme } from '@mui/material/styles'; function ResponsiveComponent() { const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down('sm')); const isTablet = useMediaQuery(theme.breakpoints.between('sm', 'md')); const isDesktop = useMediaQuery(theme.breakpoints.up('md')); // SSR: default to a value so the first render matches server output const prefersDark = useMediaQuery('(prefers-color-scheme: dark)', { defaultMatches: false, noSsr: true, }); return isMobile ? : ; } ``` ### Breakpoint helpers ```tsx // theme.breakpoints.up(key) — key and above // theme.breakpoints.down(key) — below key (exclusive) // theme.breakpoints.between(start, end) — start to end (exclusive end) // theme.breakpoints.only(key) — exactly key // In sx prop (shorthand) Desktop only content // In styled() const HiddenOnMobile = styled(Box)(({ theme }) => ({ [theme.breakpoints.down('md')]: { display: 'none', }, })); ``` --- ## Common Layout Patterns ### App shell: sidebar + main content ```tsx const DRAWER_WIDTH = 240; function AppShell() { const [mobileOpen, setMobileOpen] = React.useState(false); const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down('md')); const drawerContent = ( ); return ( t.zIndex.drawer + 1 }} > {isMobile && ( setMobileOpen(true)}> )} My App {/* Mobile: temporary drawer */} setMobileOpen(false)} ModalProps={{ keepMounted: true }} sx={{ display: { xs: 'block', md: 'none' }, '& .MuiDrawer-paper': { width: DRAWER_WIDTH }, }} > {drawerContent} {/* Desktop: permanent drawer */} {drawerContent} {/* Spacer for AppBar */} {/* Page content */} ); } ``` ### Dashboard card grid ```tsx function Dashboard() { return ( {/* Stat cards row */} {stats.map((stat) => ( ))} {/* Main content + sidebar */} Recent Activity Quick Stats Top Items ); } ``` ### Centered auth form ```tsx function LoginPage() { return ( Sign in ); } ``` ### Masonry layout ```tsx // For masonry layout, use Masonry from @mui/lab import Masonry from '@mui/lab/Masonry'; {items.map((item) => ( {item.content} ))} ``` --- ## Advanced Layout Patterns ### Responsive AppBar + Drawer Layout ```tsx import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; import Drawer from '@mui/material/Drawer'; import useMediaQuery from '@mui/material/useMediaQuery'; import { useTheme } from '@mui/material/styles'; const DRAWER_WIDTH = 240; function ResponsiveLayout({ children }: { children: React.ReactNode }) { const theme = useTheme(); const isDesktop = useMediaQuery(theme.breakpoints.up('md')); const [mobileOpen, setMobileOpen] = useState(false); const drawerContent = ( {/* spacer for AppBar height */} {navItems.map((item) => ( {item.icon} ))} ); return ( theme.zIndex.drawer + 1 }}> {!isDesktop && ( setMobileOpen(true)} aria-label="menu"> )} App {/* Mobile: temporary drawer */} {!isDesktop && ( setMobileOpen(false)} ModalProps={{ keepMounted: true }} sx={{ '& .MuiDrawer-paper': { width: DRAWER_WIDTH } }} > {drawerContent} )} {/* Desktop: permanent drawer */} {isDesktop && ( {drawerContent} )} {/* spacer */} {children} ); } ``` ### Mini Variant Drawer (Icon-Only Collapsed) ```tsx const MINI_WIDTH = 56; const FULL_WIDTH = 240; theme.transitions.create('width', { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.enteringScreen, }), '& .MuiDrawer-paper': { width: expanded ? FULL_WIDTH : MINI_WIDTH, overflowX: 'hidden', transition: 'inherit', }, }} > setExpanded(!expanded)}> {expanded ? : } {navItems.map((item) => ( {item.icon} {expanded && } ))} ``` ### Sticky Header + Scrollable Content ```tsx App {/* Scrollable content area */} {children} {/* Sticky footer */} ``` ### Dashboard Grid Layout ```tsx {/* Full-width stats row */} {stats.map((stat) => ( {stat.label} {stat.value} ))} {/* Main chart + sidebar */} {/* Full-width data table */} ```