DEV Community

Cover image for How to build a complex layout with Material UI
Armstrong Olusoji
Armstrong Olusoji

Posted on • Updated on

How to build a complex layout with Material UI

Many developers assume that Material UI (MUI) cannot build complex layouts. But this is far from true. Building complex layouts with MUI involves mapping the page and using the right elements. To that effect, MUI provides layout components such as Grid. It also allows the use of custom CSS.

This article will attempt to recreate a complex layout with MUI and custom CSS. It reads like a tutorial, but it is not. It is more like a mental model for combining MUI layout components with custom CSS. As you follow the code in your own IDE, feel free to experiment with your solutions.

Some of the MUI layout components we will use are as follows:

  • Grid
  • Box
  • Stack

In this article, you will recreate the following layout:

Image description

As you follow along, please experiment with other features from the official MUI documentation. If you have any interesting insights, please share them in the comments.

Setting the Canvas

Here we set the canvas for the layout. Here’s the code:

import React from "react";
import { Grid } from "@mui/material";
export default function ComplexLayout() {
  return (
    {/* Main div for the entire page */}
<div
  className="body"
  style={{
    padding: "10px", // Adding padding to the div
    height: "100vh", // Setting the height of the div to full viewport height
    backgroundColor: "#212121", // Setting the background color of the div
  }}
>
  {/* Grid container with items in a row */}
  <Grid container direction={"row"} spacing={3} sx={{ padding: 5 }}>
    {/* Grid item for the slideshow, takes 3 columns on large screens and extra-large screens */}
    <Grid className="side-bar-container" item lg={3} xl={3}></Grid>
    {/* Grid item for the music player, takes 9 columns on large screens and extra-large screens */}
    <Grid className="main-content-container" item lg={9} xl={9}></Grid>
  </Grid>
</div>;
  );
}

Enter fullscreen mode Exit fullscreen mode
  1. Create a div (body) as the canvas for the page. Set its background color using the style prop. The style prop is used in MUI to add custom CSS to non-MUI components. If div was a MUI component, replace style with sx.

  2. Let the body's height fill the entire screen by, setting its height to 100vh (viewport height).

  3. Inside the body, create a Grid container and arrange its items in a row.

  4. Let the Grid container have grid items. The items are side-bar-container and main-content-container.

  5. Set the width of both grid items using breakpoints:

    • side-bar-container: covers three grid columns on large screens.
    • main-content-container: covers nine grid columns on large screens.

Building side-bar-container

Here's the code for side-bar-container:

<Grid className="side-bar-container" item lg={3} xl={3}>
  {/* Sidebar container with a black background */}
  <Box
    sx={{
      backgroundColor: "#000000",
      height: "100%",
      display: "flex",
      justifyContent: "center",
    }}
  >
    {/* Box inside the sidebar container */}
    <Stack spacing={2} sx={{ margin: "20px" }}>
      {/* Image placeholder */}
      <img
        src={imageplaceholder}
        style={{ objectFit: "contain", width: "100%" }}
      />
      {/* Heading text */}
      <Typography variant={"h5"} sx={{ color: "white" }}>
        dolor sed viverra ipsum nunc aliquet
      </Typography>
      {/* Button for reading more */}
      <Button variant={"text"} sx={{ justifyContent: "flex-start" }}>
        Read more
      </Button>
    </Stack>
  </Box>
</Grid>

Enter fullscreen mode Exit fullscreen mode
  1. Create a Box component:

    • Set its height to 100% of its parent component. Since there is no explicit height for side-bar-container, the Box gets its context from the body's height which is '100vh'. Hence this Box height 100% fills the entire length of the body. Furthermore, MUI Grid items and containers derive their size from the content inside them. This means that side-bar-container will adjust its height to the Box container.
    • Set Box's display to Flex so that Box becomes the context for its nested items.
    • Justify the items inside it to the center.
  2. Nest a Stack component inside the Box.

    • Set the margin for Stack to create some separation between it and Box.
    • Set the space between the stack items.
  3. Inside the Stack, let the image be contained. Let the image also take 100% of the Stack's width

  4. The MUI Button goes to the center of the parent component by default. Change that with justifyContent

By now, the page should look like this:

1/3 of a complex layout built with material UI

That does it for side-bar-container. Up next, main-content-container.

Building main-content-container

The main content container is a bit more tricky. We need to add the following aspects:

  1. A large product image
  2. A product description page
  3. A color palette stacked on one another So far the document structure is as follows:
<div className='body'>
  <Grid container>
    <Grid item className='side-bar-container'> </Grid>
    <Grid item className='main-content-container'> </Grid>
  </Grid>
</div>

Enter fullscreen mode Exit fullscreen mode

The body has a grid container inside it. The grid container has two grid items. Namely side-bar-content, and main-content-container. You have built side-bar-content. Now you will build main-content-container. Here’s its structure:

<Grid className="main-content-container" item lg={9} xl={9}>
  <Grid container spacing={3}>
    <Grid item className="content-box"></Grid>
    <Grid item className="colorlist" sx={{ overflow: "hidden" }}></Grid>
  </Grid>
</Grid>

Enter fullscreen mode Exit fullscreen mode

Here is the text formatted in markdown:

Code explanation:

Inside main-content-container, nest a Grid container. In MUI, nesting grid containers into grid items allow us to build complex layouts. Each nested grid container is limited to the dimensions of its immediate parent.
Inside the grid container, create two grid items.
The grid items are content-box and color-list.
Building content-box
content-box has two sections. Here's the code structure:

<Grid item className="content-box">
  <Grid container spacing={3}>
    <Grid item className={"content-box-child-1"} lg={6}>
      <img src={imageplaceholder} style={{ height: "100%", width: "100%" }} />
    </Grid>
    <Grid item className={"content-box-child-2"} lg={6}>
      <Card
        sx={{
          backgroundColor: "#000000",
          height: "100%",
          borderRadius: "0",
        }}
      >
        <Stack spacing={2}>
          <CardContent>
            <Typography variant={"p"} sx={{ color: "white" }}>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
              eiusmod tempor incididunt ut labore et dolore magna aliqua.
              Volutpat commodo sed egestas egestas. A cras semper auctor neque
              vitae tempus quam pellentesque nec. Quisque id diam vel quam
              elementum pulvinar etiam non. Porta non pulvinar neque laoreet
              suspendisse. Ultrices mi tempus imperdiet nulla.
            </Typography>
          </CardContent>
          {productList.map((product) => {
            return (
              <>
                <Box
                  border={3}
                  sx={{
                    backgroundColor: "#222222",
                  }}
                >
                  <Stack
                    direction={"row"}
                    alignItems={"center"}
                    justifyContent={"space-between"}
                  >
                    <Typography
                      sx={{ color: "white" }}
                    >{`${product.name}`}</Typography>
                    <Button>Add to cart</Button>
                  </Stack>
                </Box>
              </>
            );
          })}
        </Stack>
      </Card>
    </Grid>
  </Grid>
</Grid>
Enter fullscreen mode Exit fullscreen mode

Code explanation:

  1. Nest a Grid container inside content box.
  2. Create two grid items inside the grid container. These items are content-box-child-1 and content-box-child-2
  3. For content-box-child-1, do the following:
    • Set the width to '6' grid columns on large screens. Hence, content-box-child-1 will take half the width of its parent container, main-content-container.
    • Add an image. Let it fill the entire width and length of content-box-child-1.
  4. For content-box-child-2, do the following:
    • Set the width to '6' grid columns on large screens. Hence, content-box-child-2 will take half the width of its parent container, main-content-container.
    • Create a card component inside content-box-child-2.
    • Set the height of the card to fill the entire length of content-box-child-2.
    • MUI cards have a preset border-radius. Set it to zero to overwrite the default.
    • Inside the card, create a stack. Inside Stack do the following:
    • Create CardContent.
    • Add Typography to CardContent loop through an array.
    • Render the array as follows: For each array item, create a Box component with a border of 2.
      • Inside the box, stack the name of the product and a button. Let the stack items be arranged in a row. Then do the following:
      • Justify each array item to have some space between them.
      • Align the items to the center.

By now, the layout should look like this:

2/3 of the complex layout built with material UI

Note: You set the height for the items in _content-box-child-1_ and _content-box-child-2_ to 100% of the grid item. Since these are the only Grid items in main-content-container, they will span the length of the entire page. But something interesting happens when we add another grid item to _main-content-container_'s main axis (vertical).

The 100% height content for the items in _content-box-child-1_ and _content-box-child-2_ changes context from main-content-container to just the grid item that holds that content.

Therefore, the 100% height will only be within the grid item. main-content-container will then expand its height to accommodate a new grid item along its main axis. This changes the layout of the entire page.

Building color-list

This is the second grid item inside _main-content-container_. Here's the code:

<Grid item className="color-list" sx={{ overflow: "hidden" }}>
  <Stack direction={"row"} spacing={-13}>
    {colorList.map((color) => {
      return (
        <Box
          sx={{
            width: "150px",
            height: "150px",
            backgroundColor: color,
          }}
        >
          {" "}
        </Box>
      );
    })}
  </Stack>
</Grid>

Enter fullscreen mode Exit fullscreen mode

Code explanation:

  1. Inside color-list create a Stack (not Tony Stark) component. 2.Since we have an array of over 20 colored boxes, set Stack's overflow to hidden.
  2. Stack the images in a row
  3. Add negative spacing, so each color box overlaps the color box to its left.
  4. For each array item, do the following:
    • Create a Box.
    • Set a fixed height
    • Set a fixed width
    • Set the background color

The layout should look like this:
3/3 of the complex layout built with material UI

Conclusion:

MUI layout components such as Grid, Stack, and Box are just ways that MUI can help.
There are other components not used in this article. Please refer to the official documentation.

Furthermore, MUI allows developers to use custom CSS when necessary. This article demonstrates MUI's flexibility by using CSS 'flex display'. You can also use CSS features such as 'absolute' or 'Z-index'.

But beyond MUI's flexibility, this article provides a way to think about layouts. The major principle at play here is to group related content into containers. This principle is possible because of MUI's ability to use Grid components, Grid items, and Stacks as both parent and children. This idea offers the least path to resistance.

Finally, please do your experiments with MUI. For example, this article only uses the Large breakpoint. Hence, this design is not responsive on small and medium screens. Perhaps that is one way for you to test your newfound knowledge.

Top comments (0)