'Dynamically adjust ImageList columns based on screen size?

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?



Solution 1:[1]

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

Solution 2:[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>
    )
}

Solution 3:[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>
    );
}

Solution 4:[4]

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>

Solution 5:[5]

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.

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 Stormwaker
Solution 3 Agent RX
Solution 4 Maqsood Ahmed
Solution 5 atazmin