Building a Masonry Layout in React with Infinite Scroll

Harshal Ranjhani - May 31 - - Dev Community

Masonry layouts are a popular way to display content in a grid-like structure. They are commonly used in web applications to showcase images, videos, and other types of content in an organized and visually appealing manner. In this tutorial, we will learn how to build a masonry layout in React using the react-responsive-masonry library.

What is a Masonry Layout?

A masonry layout is a grid-based layout that arranges elements in a way that optimizes the use of space. Unlike traditional grid layouts, where each row has a fixed height, a masonry layout allows elements to be placed in columns of varying heights. This creates a more dynamic and visually interesting layout that can adapt to different screen sizes and content types.

Setting Up a React Project

Before we start building our masonry layout, let's set up a new React project using vite. If you already have a React project, you can skip this step.

npm create vite@latest
Enter fullscreen mode Exit fullscreen mode

Follow the prompts to create a new React project. Once the project is created, navigate to the project directory and install the react-responsive-masonry library.

npm install react-responsive-masonry
Enter fullscreen mode Exit fullscreen mode

Install the axios library to make API requests.

npm install axios
Enter fullscreen mode Exit fullscreen mode

We will also be needing the react-infinite-scroll-component library to implement infinite scrolling.

npm install react-infinite-scroll-component
Enter fullscreen mode Exit fullscreen mode

Also, install the dotenv library to load environment variables.

npm install dotenv
Enter fullscreen mode Exit fullscreen mode

And we're finally ready to start building our masonry layout!

Implementing the Masonry Layout

We will be using the Unsplash API to fetch images for our masonry layout. You will need to sign up for a free account to get an access key.

Once you have your access key, create a new file called .env in the root of your project and add the following line:

VITE_UNSPLASH_ACCESS_KEY=<YOUR_ACCESS_KEY>
Enter fullscreen mode Exit fullscreen mode

We will be implementing the masonry layout in an infinite scroll component. So when the user scrolls down, more images will be loaded from the API. I have also written an article on Implementing Infinite Scroll in React if you want to learn more about it.

In your App.jsx file, add the following code:

import axios from "axios";
import { useEffect } from "react";
import { useState } from "react";
import InfiniteScroll from "react-infinite-scroll-component";
import Masonry, { ResponsiveMasonry } from "react-responsive-masonry";

export default function App() {
  const [images, setImages] = useState([]);
  const [page, setPage] = useState(1);

  const accessKey = import.meta.env.VITE_UNSPLASH_ACCESS_KEY;
  const fetchImages = async () => {
    try {
      const response = await axios.get(
        `https://api.unsplash.com/photos?client_id=${accessKey}&page=${page}`
      );
      console.log(response);
      setImages((prevImages) => [...prevImages, ...response.data]);
      setPage((prevPage) => prevPage + 1);
    } catch (error) {
      console.error("Error fetching images:", error);
    }
  };

  useEffect(() => {
    fetchImages();
  }, []);

  return (
    <div className="App">
      <InfiniteScroll
        dataLength={images.length}
        next={fetchImages}
        hasMore={true}
        loader={<h4>Loading...</h4>}
      >
        <ResponsiveMasonry
          columnsCountBreakPoints={{ 300: 2, 500: 3, 700: 4, 900: 5 }}
        >
          <Masonry gutter="20px">
            {images.map((image) => {
              return (
                <img
                  key={image.id}
                  src={image.urls.regular}
                  alt={image.alt_description}
                  style={{ width: "100%", borderRadius: "8px", margin: "3px" }}
                />
              );
            })}
          </Masonry>
        </ResponsiveMasonry>
      </InfiniteScroll>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now, let's understand the code:

  • We import the necessary libraries and set up the state variables for storing the images and the current page number.

  • We define states for the images and the current page number. The images state will store the images fetched from the API, and the page state will keep track of the current page number for pagination and infinite scrolling.

const [images, setImages] = useState([]);
const [page, setPage] = useState(1);
Enter fullscreen mode Exit fullscreen mode
  • We fetch images from the Unsplash API using the access key stored in the environment variable.
const fetchImages = async () => {
  try {
    const response = await axios.get(
      `https://api.unsplash.com/photos?client_id=${accessKey}&page=${page}`
    );
    console.log(response);
    setImages((prevImages) => [...prevImages, ...response.data]);
    setPage((prevPage) => prevPage + 1);
  } catch (error) {
    console.error("Error fetching images:", error);
  }
};
Enter fullscreen mode Exit fullscreen mode
  • We use the useEffect hook to fetch images when the component mounts.
useEffect(() => {
  fetchImages();
}, []);
Enter fullscreen mode Exit fullscreen mode
  • We render the images in a masonry layout using the ResponsiveMasonry and Masonry components from the react-responsive-masonry library.
<ResponsiveMasonry columnsCountBreakPoints={{ 300: 2, 500: 3, 700: 4, 900: 5 }}>
  <Masonry gutter="20px">
    {images.map((image) => {
      return (
        <img
          key={image.id}
          src={image.urls.regular}
          alt={image.alt_description}
          style={{ width: "100%", borderRadius: "8px", margin: "3px" }}
        />
      );
    })}
  </Masonry>
</ResponsiveMasonry>
Enter fullscreen mode Exit fullscreen mode

Here, gutter is the space between the images in the masonry layout, and columnsCountBreakPoints defines the number of columns based on the screen width.

  • We wrap the masonry layout in an InfiniteScroll component to implement infinite scrolling.
<InfiniteScroll
    dataLength={images.length}
    next={fetchImages}
    hasMore={true}
    loader={<h4>Loading...</h4>}
>
Enter fullscreen mode Exit fullscreen mode
  • Finally, we add some basic styling to the images to make them visually appealing.
<img
  key={image.id}
  src={image.urls.regular}
  alt={image.alt_description}
  style={{ width: "100%", borderRadius: "8px", margin: "3px" }}
/>
Enter fullscreen mode Exit fullscreen mode

And that's it! You have successfully implemented a masonry layout in React using the react-responsive-masonry library. You can further customize the layout by adding animations, lazy loading, and other features to enhance the user experience of your web applications.

If you've followed along, you should see something like this:

Masonry Layout

Conclusion

In this tutorial, we learned how to build a masonry layout in React using the react-responsive-masonry library. Masonry layouts are a great way to display content in a grid-like structure that adapts to different screen sizes and content types. By implementing a masonry layout in your web applications, you can improve the user experience and create visually appealing designs that engage your users.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .