Muhi Logo Text
AboutBlogWork With Me

MUI Grid: Building Web Layouts Responsively

Dive into the essentials of utilizing MUI Grid to build responsive web layouts in React.

Last updated on April 04, 2024

react
mui
MUI Grid

MUI Grid is an essential component for creating flexible and responsive layouts. The core mechanism of the Grid component is based on the 12-column grid system, which allows developers to create complex layouts that adapt to different screen sizes.

Throughout this tutorial, we’ll explore the fundamentals of the MUI Grid component and learn how to construct a simple and more complex responsive page layout.

Understanding the 12-Grid System

The choice of 12 as the base number for this grid system isn’t arbitrary. It’s selected for its high divisibility, allowing for a wide range of layout patterns that can quickly adapt to different screen sizes.

It’s important to clarify that when we refer to the 12-grid system, we’re not talking about a literal measurement in units of 12, 11, 10, etc. Instead, it’s a method of dividing the screen into column blocks, facilitating the arrangement and sizing of layout elements.

12 Grids Equal 100%: The entire screen width is considered as 100%, represented by 12 grids, allowing for a straightforward calculation of percentage widths.

Dividing the Grid: Divisions of the 12 grids provide flexibility in responsive layout design. For instance:

  • 6 grids represent 50% of the screen width.
  • 4 grids are equivalent to one-third (approximately 33.33%).
  • 3 grids equate to one-quarter (25%).

Basic Usage of MUI Grid

The Grid component leverages CSS Flexbox to create layouts at its core, automatically treating its children as flex items. It simplifies the process of creating responsive designs, abstracting much of the complexity behind intuitive props.

Here’s a basic example of creating a layout with a sidenav that takes up 3 out of 12 columns and a body content section that occupies the remaining 9 columns. This setup uses the xs breakpoint to apply the same layout across all screen sizes.

import React from "react";
import Grid from "@mui/material/Grid";
import Paper from "@mui/material/Paper";

const SimpleLayout = () => {
  return (
    <Grid container spacing={2}>
      <Grid item xs={3}>
        <Paper sx={{ p: 2 }}>
          {/* Left nav content goes here */}
          Side Nav
        </Paper>
      </Grid>
      <Grid item xs={9}>
        <Paper sx={{ p: 2 }}>
          {/* Body content goes here */}
          Body Content
        </Paper>
      </Grid>
    </Grid>
  );
};

export default SimpleLayout;

In this example, the container prop on the first <Grid> element sets it up as a flex container, spanning the entire width of its parent by default (100%, or 12 grids). Each child <Grid item> specifies how many of those 12 columns it should span using the xs prop. Additionally, the spacing prop adds space between the grid items.

Below is an example to understand how to achieve the equivalent outcome using CSS flex properties.

import React from "react";
import Box from "@mui/material/Box";
import Paper from "@mui/material/Paper";

const SimpleLayoutWithBox = () => {
  return (
    <Box display="flex" width="100%" gap={2}>
      <Box width="25%" flexShrink={0}>
        <Paper sx={{ p: 2 }}>Side Nav</Paper>
      </Box>
      <Box width="75%">
        <Paper sx={{ p: 2 }}>Body Content</Paper>
      </Box>
    </Box>
  );
};

export default SimpleLayoutWithBox;

In this Box-based layout, we manually set the display to flex for the container and define the width of each child. This requires additional props such as flexShrink to prevent the left nav from shrinking on smaller screens, mimicking the behavior of Grid’s xs prop.

Basic Responsive Layout

The Grid component is equipped with a built-in responsive breakpoint system, allowing us to define layouts that easily adapt to different viewport sizes.

  • xs: Extra-small devices (portrait phones)
  • sm: Small devices (landscape phones)
  • md: Medium devices (tablets)
  • lg: Large devices (desktops)
  • xl: Extra-large devices (large desktops)

Specifying a breakpoint on a Grid item means the layout will apply for that breakpoint and all larger screens unless overridden by a larger breakpoint.

Let’s enhance our previous example by adjusting the size of the side nav and main content based on the screen size.

import React from "react";
import Grid from "@mui/material/Grid";
import Paper from "@mui/material/Paper";

const ResponsiveLayout = () => {
  return (
    <Grid container spacing={2}>
      <Grid item xs={12} sm={4} md={3} lg={2}>
        <Paper sx={{ p: 2 }}>Side Nav</Paper>
      </Grid>

      <Grid item xs={12} sm={8} md={9} lg={10}>
        <Paper sx={{ p: 2 }}>Main Content</Paper>
      </Grid>
    </Grid>
  );
};

export default ResponsiveLayout;

In this code example:

  • The side nav starts as a full-width block on extra-small devices (xs={12}) and gradually decreases in width relative to the screen size, moving to 4/12 on small screens (sm={4}), 3/12 on medium screens (md={3}), and 2/12 on large screens (lg={2}).
  • Conversely, the main content area increases its share of the available screen width as the device size increases.

This responsive design ensures that on smaller screens, the left nav and main content stack on top of each other for optimal space and readability.

In the live example above, we can use the resize handle in the bottom right corner to see how the layout changes based on the screen size.

Creating Responsive Cards

Displaying content within cards is a widely used design pattern, ideal for showcasing posts, images, products, and more.

We’ll begin by creating a Container with a maxWidth of “lg” to constrain the width on larger screens, ensuring our card layout remains aesthetically pleasing without stretching too wide on larger monitors. For a deeper understanding of the Container component, refer to this detailed tutorial.

<Container maxWidth="lg">{/* Cards go here */}</Container>

Next, we’ll populate this container with a responsive grid of cards. Each card will be wrapped in a Grid item component that specifies the number of columns it should span on different screen sizes.

<Grid item key={index} xs={12} sm={6} md={4} lg={3}>
  <Card>{/* Card content goes here */}</Card>
</Grid>
  • Large screens (lg): 3 columns (12/3 = 4 cards per row)
  • Medium screens (md): 4 columns (12/4 = 3 cards per row)
  • Small screens (sm): 6 columns (12/6 = 2 cards per row)
  • Extra-small screens (xs): Full width (1 card per row)

Finally, we’ll create a list of cards by mapping over an array of card data. Each card will contain a CardMedia component for images, a CardContent component for text content, and Typography components for titles and descriptions.

import React from "react";
import {
  Container,
  Grid,
  Card,
  CardMedia,
  CardContent,
  Typography,
} from "@mui/material";

const CardList = () => {
  return (
    <Container maxWidth="lg">
      <Grid container spacing={2}>
        {[1, 2, 3, 4, 5, 6].map((card, index) => (
          <Grid item key={index} xs={12} sm={6} md={4} lg={3}>
            <Card>
              <CardMedia
                component="img"
                height="140"
                image="https://via.placeholder.com/200"
                alt={`Card ${index + 1}`}
              />
              <CardContent>
                <Typography gutterBottom variant="h5" component="div">
                  Card Title {index + 1}
                </Typography>
                <Typography variant="body2" color="text.secondary">
                  This is a media card. You can use this section to describe the
                  content.
                </Typography>
              </CardContent>
            </Card>
          </Grid>
        ))}
      </Grid>
    </Container>
  );
};

export default CardList;

In this example, if we resize the preview window, we can see how the number of cards per row changes based on the screen size.

Creating a Page Layout

Creating a comprehensive page layout involves integrating various components and responsive design techniques we discussed earlier. Let’s break it down and highlight the new additions.

Main Structure

The page component is divided into four main sections: header, sidenav, main content, and footer. Each section is wrapped in a Grid item component that specifies the number of columns it should span on different screen sizes.

import React from "react";
import { Container, Grid, Typography, Paper } from "@mui/material";

const PageLayout = () => {
  return (
    <Container maxWidth="lg">
      <Grid container spacing={3}>
        {/* Header */}
        <Grid item xs={12}>
          <Paper sx={{ p: 2, textAlign: "center" }}>
            <Typography variant="h4">Page Header</Typography>
          </Paper>
        </Grid>
        {/* sidenav */}
        <Grid item xs={12} sm={4} md={3} lg={2}>
          <Paper sx={{ p: 2 }}>Side Nav</Paper>
        </Grid>
        {/* Main Content */}
        <Grid item xs={12} sm={8} md={9} lg={10}>
          <Paper sx={{ p: 2 }}>Main Content</Paper>
        </Grid>
        {/* Footer */}
        <Grid item xs={12}>
          <Paper sx={{ p: 2, textAlign: "center" }}>
            <Typography variant="body1">Page Footer</Typography>
          </Paper>
        </Grid>
      </Grid>
    </Container>
  );
};

export default PageLayout;

The logic behind the layout is similar to the responsive layout example we discussed earlier. The header and footer sections span the full width of the screen, while the sidenav and main content sections adjust their width based on the screen size.

Sidenav

The sidenav will mock a list of filters users can select to filter the cards in the main content. We will populate the sidenav with a List component containing ListItem for each filter. To save space, the layout will switch to a Select dropdown on smaller screens.

const theme = useTheme();
const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm"));
const filters = ["Filter 1", "Filter 2", "Filter 3"];

return (
  <Grid item xs={12} sm={4} md={3} lg={2}>
    <Paper sx={{ p: 2 }}>
      {isSmallScreen ? (
        <FormControl fullWidth>
          <Select value={selectedFilter} onChange={handleFilterChange}>
            {filters.map((filter, index) => (
              <MenuItem key={index} value={filter}>
                {filter}
              </MenuItem>
            ))}
          </Select>
        </FormControl>
      ) : (
        <List>
          {filters.map((filter, index) => (
            <ListItem button key={index}>
              <ListItemText primary={filter} />
            </ListItem>
          ))}
        </List>
      )}
    </Paper>
  </Grid>
);

In this example, we use the useTheme and useMediaQuery hooks to determine the screen size. We render a Select dropdown if the screen is small (down to 600px). Otherwise, we display List.

Complete Page Layout

Finally, let’s create the PageLayout component, which integrates the header, sidenav, main content (the cards we created earlier part), and footer sections into a cohesive layout.

import React from "react";
import {
  Container,
  Grid,
  Paper,
  Typography,
  List,
  ListItem,
  ListItemText,
  FormControl,
  Select,
  MenuItem,
  Card,
  CardContent,
  CardMedia,
  useTheme,
  useMediaQuery,
} from "@mui/material";

function PageLayout() {
  const theme = useTheme();
  const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm"));

  const filters = ["Filter 1", "Filter 2", "Filter 3"];

  return (
    <Container maxWidth="lg">
      <Grid container spacing={3}>
        {/* Header */}
        <Grid item xs={12}>
          <Paper sx={{ p: 2, textAlign: "center" }}>
            <Typography variant="h4">Page Header</Typography>
          </Paper>
        </Grid>
        {/* Sidebar */}
        <Grid item xs={12} sm={4} md={3} lg={2}>
          <Paper sx={{ p: 2 }}>
            {isSmallScreen ? (
              <FormControl fullWidth>
                <Select>
                  {filters.map((filter, index) => (
                    <MenuItem key={index} value={filter}>
                      {filter}
                    </MenuItem>
                  ))}
                </Select>
              </FormControl>
            ) : (
              <List>
                {filters.map((filter, index) => (
                  <ListItem button key={index}>
                    <ListItemText primary={filter} />
                  </ListItem>
                ))}
              </List>
            )}
          </Paper>
        </Grid>
        {/* Main Content */}
        <Grid item xs={12} sm={8} md={9} lg={10} container spacing={2}>
          {[1, 2, 3, 4].map((card, index) => (
            <Grid item key={index} xs={12} sm={6} md={4} lg={3}>
              <Card>
                <CardMedia
                  component="img"
                  height="140"
                  image="https://via.placeholder.com/200"
                  alt="placeholder"
                />
                <CardContent>
                  <Typography gutterBottom variant="h5" component="div">
                    Card Title {index + 1}
                  </Typography>
                  <Typography variant="body2" color="text.secondary">
                    This is a media card. You can use this section to describe
                    the content.
                  </Typography>
                </CardContent>
              </Card>
            </Grid>
          ))}
        </Grid>
        {/* Footer */}
        <Grid item xs={12}>
          <Paper sx={{ p: 2, textAlign: "center" }}>
            <Typography variant="body1">Page Footer</Typography>
          </Paper>
        </Grid>
      </Grid>
    </Container>
  );
}

export default PageLayout;

We can see how the layout adapts to different screen sizes when adjusting the screen size. The sidenav switches from a list to a dropdown on smaller screens, and the number of cards per row changes based on the screen size.

Summary

The MUI Grid component is a powerful way for creating responsive layouts in React applications. By leveraging the 12-column grid system and responsive breakpoints, we can easily design flexible, user-friendly interfaces that adapt to various screen sizes.

Bye for now and happy coding!

If you enjoyed this post, I regularly share similar content on Twitter. Follow me @muhimasri to stay up to date, or feel free to message me on Twitter if you have any questions or comments. I'm always open to discussing the topics I write about!

Recommended Reading

Learn how to create encapsulated and reusable Fieldset component with Material UI (MUI) and React.

react
mui

Discussion

Upskill Your Frontend Development Techniques 🌟

Subscribe to stay up-to-date and receive quality front-end development tutorials straight to your inbox!

No spam, sales, or ads. Unsubscribe anytime you wish.

© 2024, Muhi Masri