13

I've tried using ImageList component instead of Grid as I just need a grid of photos with titles and it seems to be the whole point of ImageList. My issue is that unlike with Grid I cannot pass breakpoint props for different screen sizes (which I find weird as this would seem logical) so that I can get different count of columns on different screens. What would be the best approach to adjust number of columns based on screen size?

NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
Stormwaker
  • 381
  • 2
  • 12

8 Answers8

10

ImageList uses CSS grid and needs the col prop to set the grid-template-columns but without any responsive API baked in. You can swap the ImageList with a Box component with the display set to grid, and uses the sx prop to declare the column template value depend on the screen size, but first let define some breakpoints:

const theme = createTheme({
  breakpoints: {
    values: {
      mobile: 0,
      bigMobile: 350,
      tablet: 650,
      desktop: 900
    }
  }
});

Then in the component, you can start using it like this:

import ImageListItem, { imageListItemClasses } from "@mui/material/ImageListItem";
<ThemeProvider theme={theme}>
  <Box
    sx={{
      display: "grid",
      gridTemplateColumns: {
        mobile: "repeat(1, 1fr)",
        bigMobile: "repeat(2, 1fr)",
        tablet: "repeat(3, 1fr)",
        desktop: "repeat(4, 1fr)"
      }
      // standard variant from here:
      // https://github.com/mui-org/material-ui/blob/3e679ac9e368aeb170d564d206d59913ceca7062/packages/mui-material/src/ImageListItem/ImageListItem.js#L42-L43
      [`& .${imageListItemClasses.root}`]: {
        display: "flex",
        flexDirection: "column"
      }
    }}
  >
    {itemData.map((item) => <ImageListItem {...}/>)}
  </Box>
</ThemeProvider>

Live Demo

Codesandbox Demo

References

NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
  • Is there a way to access default breakpoints instead of defining values ourselves here? – Stormwaker Sep 30 '21 at 15:51
  • 1
    @Stormwaker yes, there is. See the default breakpoint values [here](https://mui.com/customization/breakpoints/#default-breakpoints). I don't use it because it doesn't look nice in the live demo – NearHuscarl Sep 30 '21 at 15:54
  • @Stormwaker you don't even have to import `ThemeProvider` or `createTheme` if you only use the default values in theme just FYI. – NearHuscarl Sep 30 '21 at 15:55
  • 1
    Removing `ImageList` as parent of `ImageListItem` makes it behave a bit clunky. For example if I set `ImageListItemBar` `position` to `below` it won't appear (being hiden under next row). Of course I can set `rowGap` of this `Box` high enough so it will reveal the text again, but I'm basically starting to recreate whole `ImageList` and `ImageListItem` from the base after more of these additions. I'll stick with my solution as it allows me to keep built in features of `ImageList`. – Stormwaker Sep 30 '21 at 18:59
  • 1
    @Stormwaker see my updated answer, I copy the relevant styles from `ImageList` – NearHuscarl Oct 01 '21 at 04:48
6

I used the useMediaQuery hook to get the cols props for the ImageList component.

import { ImageList, ImageListItem, useMediaQuery } from '@mui/material';

function Gallery() {
const matches = useMediaQuery('(min-width:600px)');
return (
    <ImageList cols={matches ? 3 : 2} variant="masonry">
      <ImageListItem>
       ...
      </ImageListItem>
    </ImageList>
  );
}
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 07 '22 at 07:57
  • This is the best answer IMHO, if ok with a single breakpoint. Works well and is a simple solution. Thanks for sharing! – Roland Ayala Oct 30 '22 at 10:00
3

I spotted a similar problem. The ImageList renders a <ul> tag in DOM. Hence I created my own ImageList styled <ul> component which works fine with ImageListItem. Here as per the gridTemplateColumns attribute for screens with display size sm will show 2 images, display size md will show 4 images and display size lg will show 5 images.

import * as React from 'react';
import ImageListItem from '@mui/material/ImageListItem';
import { styled } from '@mui/material/styles';

const ImageGalleryList = styled('ul')(({ theme }) => ({
    display: 'grid',
    padding: 0,
    margin: theme.spacing(0, 4),
    gap: 8,
    [theme.breakpoints.up('sm')]: {
        gridTemplateColumns: 'repeat(2, 1fr)'
    },
    [theme.breakpoints.up('md')]: {
        gridTemplateColumns: 'repeat(4, 1fr)'
    },
    [theme.breakpoints.up('lg')]: {
        gridTemplateColumns: 'repeat(5, 1fr)'
    },
}));

export default function ImageGallery({imageData}) {
    return (
        <ImageGalleryList>
            {itemData.map((item) => (
                <ImageListItem key={item.img}>
                    // Replace this with your ImageListItem
                </ImageListItem>
            ))}
        </ImageGalleryList>
    );
}
Agent RX
  • 31
  • 1
  • Seems good. Although the best way to go about it is probably just opening an issue on `material-ui` github repo and modifying the `ImageList` component to take breakpoints as parameters. – Stormwaker Jan 08 '22 at 17:43
2

This solution I came up with works, but seems like a lot of lines for something that Grid handles out of the box. Doesn't ImageList have some built in responsive design implementation?

export function Example(props) {
    // not sure if there is a way to get something like this dictionary from React?
    const breakpoints = {
        xs: 0,
        sm: 600,
        md: 960,
        lg: 1280,
        xl: 1920
    }

    const getColumns = (width) => {
        if (width < breakpoints.sm) {
            return 2
        } else if (width < breakpoints.md) {
            return 3
        } else if (width < breakpoints.lg) {
            return 6
        } else if (width < breakpoints.xl) {
            return 7
        } else {
            return 8
        }
    }

    const [columns, setColumns] = useState(getColumns(window.innerWidth))
    const updateDimensions = () => {
        setColumns(getColumns(window.innerWidth))
    }

    useEffect(() => {
        window.addEventListener("resize", updateDimensions);
        return () => window.removeEventListener("resize", updateDimensions);
    }, []);

    return (
        <ImageList cols={columns}>
          {/* list items ... */}
        </ImageList>
    )
}
Stormwaker
  • 381
  • 2
  • 12
  • This worked well for me too. Did you ever find out whether MUI has a simpler solution, akin to what Grid does out of the box? – Rylan Schaeffer Jun 25 '22 at 17:32
  • 1
    @RylanSchaeffer In the end I felt like ImageList is not very thought-out and created layout to suit my needs using Grid component. – Stormwaker Jun 25 '22 at 18:34
1

Instead of using ImageList I used "Image masonry" seems to work. https://mui.com/material-ui/react-masonry/#main-content

<Masonry columns={{ xs: 1, sm: 2, md: 3, lg: 4, xl: 5 }} spacing={{ xs: 1, sm: 2 }}>

Image masonry This example demonstrates the use of Masonry for images. Masonry orders its children by row. If you'd like to order images by column, check out ImageList.

atazmin
  • 4,757
  • 1
  • 32
  • 23
  • How does one import Masonry? I tried `import Masonry from '@mui/lab/Masonry';` and received `export 'default' (imported as 'Masonry') was not found in '@mui/lab/Masonry' (module has no exports)` – Rylan Schaeffer Jun 25 '22 at 17:31
  • 1
    @RylanSchaeffer I had to add https://www.npmjs.com/package/@mui/lab – atazmin Jun 25 '22 at 18:22
1

This code snippet utilizes Material-UI's ImageList and ImageListItem components to create a responsive grid of images with customizable gaps and column widths.

<ImageList
  gap={12}
  sx={{
    gridTemplateColumns:
      'repeat(auto-fill, minmax(280px, 1fr))!important',
  }}
>
  <ImageListItem>{your_item_comes_here}<ImageListItem>
</ImageList>

<ImageList>: This tag is the beginning of the ImageList component provided by Material-UI, which is a container for ImageListItem components.

gap={12}: This attribute sets the gap between the items in the image list to 12 pixels.

sx={{...}}: The sx attribute is a Material-UI specific attribute that allows you to apply custom CSS styles to the component. It is a shorthand syntax for writing inline styles.

gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))!important': This style rule is a CSS Grid Layout property that defines the columns for the component.

repeat(auto-fill, minmax(280px, 1fr)) tells the browser to create as many columns as it can fit within the container while maintaining a minimum width of 280 pixels and magximum width of 1fr for each column. The !important declaration gives this rule higher specificity, ensuring it takes precedence over any other conflicting CSS rules.

<ImageListItem>{your_item_comes_here}<ImageListItem>: This pair of tags represents the ImageListItem component provided by Material-UI. You should replace {your_item_comes_here} with the actual content (e.g., an image) you want to display within the list item.

</ImageList>: This tag is the ending tag for the ImageList component.

1

Very easy

useBreakPoint.Js

import { useMediaQuery, useTheme } from '@mui/material';

const useBreakpoint = () => {
  const theme = useTheme();
  const breakpoints = theme.breakpoints.keys;
  const matchingBreakpoint = breakpoints.filter((key) => useMediaQuery(theme.breakpoints.only(key)));
  
  return matchingBreakpoint;
};

export default useBreakpoint;

Home.js

export default function Home() {
  const breakpoint = useBreakpoint();
  const cols = {
    xs: 2,
    sm: 3,
    md: 4,
    lg: 6,
    xl: 8,
  }[breakpoint];

    return (
        <ImageList cols={cols}>
            {itemData.map((item) => (
                <ImageListItem key={item.img}>
                    // Replace this with your ImageListItem
                </ImageListItem>
            ))}
        </ImageList>
    );
}
Fatih Erol
  • 633
  • 2
  • 8
  • 22
0

I resolved this by overriding column-count property on ImageList (root component)

So, you can add breakpoints using sx props

<ImageList
  sx={{
    columnCount: {
      xs: '1 !important',
      sm: '2 !important',
      md: '3 !important',
      lg: '4 !important',
      xl: '5 !important',
    },
  }}
>
  {/* list items ... */}
</ImageList>
Maqsood Ahmed
  • 1,853
  • 1
  • 10
  • 18