'MUI - V5 Grid System spacing not producing gutters between Grid Items

I am just learning Material UI with react. Starting with V5. I have a basic 12 column grid just to learn this. The spacing is just not working properly. It is just creating a weird padding on the grid items where the items themselves are getting internal padding and I don't see the gutters.

I tested it on the most basic example to ensure its as simple to see:

Here is the code:

//MuiGrid.js
import React from 'react'
import {Grid, Typography, Box} from '@mui/material';

export default function MuiGrid() {
  return (
    <Box>
        <Typography variant='h2'>MUI Grid!</Typography>      
        {/* Testing Grid Spacing */}
        <Box component='section'>
            <Typography variant='h5'>Testing Spacing</Typography>
            <Box>
              <Grid container spacing={2}>
                  <Grid item sx={{backgroundColor: 'primary.dark'}}>Item 1</Grid>
                  <Grid item sx={{backgroundColor: 'primary.main'}}>Item 2</Grid>
                  <Grid item sx={{backgroundColor: 'primary.light'}}>Item 3</Grid>
              </Grid>
            </Box>
        </Box>

    </Box>
  )
}

I have managed to reproduce this with my first sandbox. Here is the link: https://codesandbox.io/s/quirky-lake-50pqf5?file=/src/Demo.tsx

Here are the screenshots before & after spacing is applied, it shows just odd padding:

Without any spacing:

Given a spacing of 5, as you can see there are no gutters, its just creating an odd padding, its even gone over the heading

All the documentation is very vague and only shows basic information & I can't see what I have done wrong here

Edit 2:

I tried to make spacing 0 and add padding as suggested by the answer, that works well to provide padding but I can't get gutters

See code:

<Box component='section'>
              <Typography variant='h5'>Testing Spacing</Typography>
              <Box>
                <Grid container spacing={0}>
                    <Grid p={1} item sx={{backgroundColor: 'primary.dark'}}> <Box>Item 1</Box></Grid>
                    <Grid p={1} item sx={{backgroundColor: 'primary.main'}}><Box>Item 2</Box></Grid>
                    <Grid p={1} item sx={{backgroundColor: 'primary.light'}}><Box>Item 3</Box></Grid>
                </Grid>
              </Box>
          </Box>

Result:

enter image description here

Also here is the link to GitHub issue I have created: https://github.com/mui/material-ui/issues/31244

I have added background to the container and coloured the box inside the items, this provides a better idea about what is happening:

 <Box component='section'>
              <Typography variant='h5'>Testing Spacing</Typography>
              <Box>
                <Grid container spacing={2}
                  sx={{backgroundColor: 'secondary.main'}}
                >
                    <Grid  item> <Box sx={{backgroundColor: 'primary.dark'}}>Item 1</Box></Grid>
                    <Grid  item><Box sx={{backgroundColor: 'primary.main'}}>Item 2</Box></Grid>
                    <Grid  item><Box sx={{backgroundColor: 'primary.light'}}>Item 3</Box></Grid>
                </Grid>
              </Box>
          </Box>

Result:

Edit 3, colouring the box inside the grid item



Solution 1:[1]

Spacing can indeed be a bit confusing with Grid. I usually set spacing(0) for the Grid container and handle padding / margin by the content of each Grid item

 <Grid container spacing={0}>
    <Grid item>
       <Box p={1}>Item 1</Box> // use m={1} if you want margin
    </Grid>
    <Grid item>
       <Box p={1}>Item 2</Box>
    </Grid>    
    <Grid item>
       <Box p={1}>Item 3</Box>
    </Grid>
</Grid>

Alternatively, if you want to use spacing(2) you can account for the spacing's offset and wrap your Grid container in a Box that manages this offset.

<Box m={xs ? -1 : -3}> // pseudo code check
  <Grid container spacing={2}>
    <Grid item xs={12} sm={4}>Item 1</Grid>
    <Grid item xs={12} sm={4}>Item 2</Grid>    
    <Grid item xs={12} sm={4}>Item 3</Grid>
  </Grid>
</Box>

Personally, I'm not a huge fan of this approach. I like using Grid for mobile flexibility and with the margin-offset approach, you have to keep good track of how many grid items you have per row, and then adjust the box margin based on this.

Solution 2:[2]

The behavior of the MUI grid spacing is a bit unexpected since it moves the grid items to the bottom right. To keep the grid items centered you'll need to manually add paddings to the right and bottom of the container or items. One option is simply to add the same amount of spacing as padding to the right and bottom of the grid. The drawback is that you' ll have to calculate the proper amount of padding, if your grid container itself has already a padding.

The second option is to make the grid items context aware and add right padding to the last items in a grid row when the row is full. This will still leave you with the task to calculate the proper bottom padding of the grid. It would be more intuitive if the calculations of the right and bottom paddings were done internally by the MUI grid.

However, here is a simple Wrapper component that adjusts the right padding of the outer grid items in order to center the items horizontally. You'll only have to add addtional padding at the bottom of the grid.

EDIT

Actually I realized that the intial approach with adding padding just to the last columns also changes the width of the last element. So here is a much simpler approach by overriding the MUI spacing and just add half of the spacing to the left and right of all the elements. The code becomes also much simpler.

import { Breakpoint, GridSize, GridSpacing } from '@mui/material';
import { FC, ReactElement, cloneElement } from 'react';

type GridWrapperSettings = {
  spacing: GridSpacing;
  columns: { [key in Breakpoint]?: GridSize };
};

export const GridWrapper: FC<GridWrapperSettings & { children?: ReactElement }> = (props) => {
  if (!props.children) {
    return <></>;
  }

  // remove MUI spacing
  const grid = cloneElement(props.children, {
    spacing: 0,
  });

  // adjust items
  const items = grid.props.children.map((c) => {
    return cloneElement(c, {
      // assign responsive column settings
      xs: props.columns.xs,
      sm: props.columns.sm,
      md: props.columns.md,
      lg: props.columns.lg,
      xl: props.columns.xl,
      // assign half of the spacing on each side and a full spacing at the bottom
      sx: {
        pr: 0.5 * (props.spacing as number),
        pl: 0.5 * (props.spacing as number),
        pb: props.spacing,
        },
      },
    });
  });

  return cloneElement(grid, {
    children: items,
  });
};

When using you only have to set the spacing and columns once in the wrapper:

<GridWrapper spacing={4} columns={{ xs: 12, md: 6}}>
   <Grid
     container
     direction="row"
     justifyContent="center"
     alignItems="stretch"
    >
      <Grid item>My Item</Grid>
      <Grid item>My Item</Grid>
    </Grid>
</GridWrapper>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2