Search code examples
reactjsflexboxcss-gridstyled-components

How can I get alternate image of array of images with different height using grid or flex?


I have array of object ImageKist where I would like to show alternate image with different heights using grid or flex as shown below:enter image description here

How can I achieve above pattern using grid or flex?

Install styled-component as follow: npm install --save styled-components

Current result is as below where images should come from left to right where I have used grid but its fine if we get the desired result with flex. enter image description here

Below is my code with alternate images:

import styled from "styled-components";
import React from "react";

const App = () => {
const imageList = [
{
  id: "1",
  url: "https://images.unsplash.com/photo-1558979158-65a1eaa08691?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80",
},
{
  id: "2",
  url: "https://images.unsplash.com/photo-1572276596237-5db2c3e16c5d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80",
},
{
  id: "3",
  url: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1353&q=80",
},
{
  id: "4",
  url: "https://images.unsplash.com/photo-1551009175-8a68da93d5f9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1351&q=80",
},
{
  id: "5",
  url: "https://images.unsplash.com/photo-1549880338-65ddcdfd017b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80",
},
{
  id: "6",
  url: "https://res.cloudinary.com/diqqf3eq2/image/upload/v1586883334/person-1_rfzshl.jpg",
},
{
  id: "7",
  url: "https://res.cloudinary.com/diqqf3eq2/image/upload/v1586883409/person-2_np9x5l.jpg",
},
{
  id: "8",
  url: "https://res.cloudinary.com/diqqf3eq2/image/upload/v1586883417/person-3_ipa0mj.jpg",
},
];

return (
<Wrapper>
  {imageList.map((image: any, index) => (
    <>
      {index % 2 === 0 ? (
        <ImageWrapper key={image.id}>
          <ImageStyled src={image.url} alt={image.url} />
          <H4Styled>{image.id}</H4Styled>
        </ImageWrapper>
      ) : (
        <ImageWrapper1 key={image.id}>
          <ImageStyled src={image.url} alt={image.url} />
          <H4Styled>{image.id}</H4Styled>
        </ImageWrapper1>
      )}
    </>
  ))}
</Wrapper>
);
};

export default App;

const Wrapper = styled.div`
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 80px;
gap: 30px;
`;

const ImageWrapper = styled.div`
overflow: hidden;
position: relative;
grid-row: span 2;
`;

const ImageWrapper1 = styled.div`
overflow: hidden;
position: relative;
grid-row: span 3;
`;

const ImageStyled = styled.img`
min-width: 100%;
height: 100%;
border-radius: 20px;
`;

const H4Styled = styled.h4`
position: absolute;
top: 16px;
color: white;
`;

Solution

  • Update

    Only quickly tested but this seems to work for me in TypeScript:

    interface ImageWrapperProps {
      index: number;
    }
    

    And apply the interface to the component using index as prop:

    const ImageWrapper = styled.div`
      overflow: hidden;
      position: relative;
      border-radius: 20px;
      grid-row: ${({ index }: ImageWrapperProps) =>
        (index + 1) % 2 === 0 ? 'span 3' : 'span 2'};
      grid-column: ${({ index }: ImageWrapperProps) =>
        (index + 2) % 6 === 0 ? '2' : 'auto'};
    `;
    

    It probably depends on configurations, so if it does not work perhaps also reference some suggestions to use TypeScriptt with styled-components from this question.

    Also if not done yet perhaps try install @types/styled-components in the project:

    npm install @types/styled-components
    

    Original

    It seems that the props of styled-components can be used to simplify the logic and also manage the placement of images based on the index.

    The below example pass the index as prop to ImageWrapper and change the grid-row and grid-column based on index as a condition, so there is no need to define 2 components for it.

    The current condition works for a 3 column image list, but it can be adjusted in the styles to fit other layout, if needed.

    Simplified live demo of example: stackblitz

    Perhaps try:

    const Wrapper = styled.div`
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-auto-rows: 80px;
    gap: 30px;
    grid-auto-flow: row dense;
    `;
    
    // 👇 Use prop here to change style by condition
    const ImageWrapper = styled.div`
    overflow: hidden;
    position: relative;
    border-radius: 20px;
    grid-row: ${({ index }) => ((index + 1) % 2 === 0 ? 'span 3' : 'span 2')};
    grid-column: ${({ index }) => ((index + 2) % 6 === 0 ? '2' : 'auto')};
    `;
    
    const ImageStyled = styled.img`
    width: 100%;
    height: 100%;
    object-fit: cover;
    `;
    
    const H4Styled = styled.h4`
    position: absolute;
    top: 16px;
    color: white;
    `;
    

    And in the output:

    <Wrapper>
      {imageList.map((image, index) => (
        <ImageWrapper key={image.id} index={index}>
          <ImageStyled src={image.url} alt={image.url} />
          <H4Styled>{image.id}</H4Styled>
        </ImageWrapper>
      ))}
    </Wrapper>