Building a Masonry Layout in React with Infinite Scroll

May 31, 2024 (3mo ago)

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

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

Install the axios library to make API requests.

npm install axios

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

npm install react-infinite-scroll-component

Also, install the dotenv library to load environment variables.

npm install dotenv

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>

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>
  );
}

Now, let's understand the code:

const [images, setImages] = useState([]);
const [page, setPage] = useState(1);
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();
}, []);
<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>

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

<InfiniteScroll
    dataLength={images.length}
    next={fetchImages}
    hasMore={true}
    loader={<h4>Loading...</h4>}
>
<img
  key={image.id}
  src={image.urls.regular}
  alt={image.alt_description}
  style={{ width: "100%", borderRadius: "8px", margin: "3px" }}
/>

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.