How to build a complex layout with Material UI
Tags: materialui, javascript, react, frontend
Published: 2024-04-10 22:37:11

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0v3hy262zkpxlieed062.png)

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: 

```js
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>;
  );
}

```

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_:

```js
<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>

```
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ght1i6czxmeu60yyyxh.png)

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:

```js
<div className='body'>
  <Grid container>
    <Grid item className='side-bar-container'> </Grid>
    <Grid item className='main-content-container'> </Grid>
  </Grid>
</div>

```

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: 

```js
<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>

```

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:

```js
<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>
```
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1yrqn7vxl9h4jfqj82wj.png)

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:

```js
<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>

```
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.
3. Stack the images in a row
4. Add negative spacing, so each color box overlaps the color box to its left.
5. 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ktv5kidx43799njk6hta.png)

## 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.