Skip to content

How to Build a Masonry Gallery in Next.js With Cloudinary

A Masonry gallery layout is an elegant way to display images. Each item is arranged in a staggered grid, allowing for more creative space utilization. I’ve always found this layout to be a great choice, especially for smaller collections of images because it makes the most of available space while keeping the presentation visually interesting.

In this blog post, I’ll show you how to create a simple Masonry gallery using Next.js and Cloudinary to host and optimize the images. We’ll also use Tailwind CSS to handle the styling quickly and efficiently, keeping things simple but effective. By the end, you’ll have a neat and responsive gallery layout that looks great with minimal effort.

masonry gallery in a desktop device

To get started, I used the command npx create-next-app@latest and followed the prompts. One of the nice things about this setup is that it already offers an option to include Tailwind CSS during the installation, so no need to set that up separately.

Once the project was ready, I added the next-cloudinary package by running:

npm install next-cloudinary

After that, I set the NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME environment variable in my .env.local file so Cloudinary could adequately handle the image hosting. With that in place, I was ready to start building the Masonry gallery.

Now that the project setup is complete let’s dive into building the components for the Masonry gallery. The gallery will consist of two main parts: MasonryGallery and MasonryItem, with a custom CldImage component for optimized image delivery using Cloudinary.

First, we’ll create the MasonryGallery and MasonryItem components, here’s the code for both:

import React from "react";
import "./index.css";
import CustomImage from "@/components/cloudinary-image/CustomImage.client";

export function MasonryGallery({ children }: { children: React.ReactNode }) {
  return <div className="masonry-gallery my-4">{children}</div>;
}

export function MasonryItem({ src, alt }: { src: string; alt: string }) {
  return (
    <div className="masonry-item">
      <CustomImage src={src} alt={alt} className="rounded-lg" />
    </div>
  );
}
Code language: JavaScript (javascript)

The MasonryGallery component wraps the gallery layout with simple styling using the masonry-gallery class. The MasonryItem component receives the image source and alt text as props and passes them to the CustomImage component.

Speaking of CustomImage, this is where we use Cloudinary to optimize the image delivery:

"use client";
import { CldImage } from "next-cloudinary";

export default function CustomImage({
  src,
  alt,
  className = "",
}: {
  src: string;
  alt: string;
  className?: string;
}) {
  return (
    <CldImage
      src={src}
      alt={alt}
      width="500"
      height="500"
      crop={{
        type: "auto",
        source: true,
      }}
      className={className}
    />
  );
}
Code language: JavaScript (javascript)

This component leverages the CldImage from next-cloudinary to ensure images are delivered in the best format and quality for the user’s device. I’ve set the width and height to 500x500 for this example, and the auto cropping ensures Cloudinary optimizes the images on the fly.

Next, apply the CSS Grid layout logic to get that Masonry look. Here’s the CSS for the gallery:

.masonry-gallery {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: 1fr;
  gap: 1rem;

  img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
}

.masonry-item {
  grid-row: span 1;
}

.masonry-item:nth-child(1),
.masonry-item:nth-child(5) {
  grid-row: span 2;
}

The key here is using CSS Grid to define a 2-column layout and controlling the row span dynamically based on the order of the images. Using the nth-child() selector, I can make specific images take up more vertical space, giving the layout a staggered Masonry feel.

Now that we’ve set up the gallery components, it’s time to put everything together in the home page. For this example, I’m using some sample public images from Cloudinary, but in a real-world scenario, you’d replace these with images from your Cloudinary account.

Here’s the code for the home page component:

import {
  MasonryGallery,
  MasonryItem,
} from "@/components/masonry-gallery/MasonryGallery";

export default function Home() {
  return (
    <div className="border-4 rounded-lg border-purple-500 p-8 max-w-screen-lg">
      <MasonryGallery>
        <MasonryItem src="cld-sample" alt="sample image 1" />
        <MasonryItem src="cld-sample-2" alt="sample image 2" />
        <MasonryItem src="cld-sample-3" alt="sample image 3" />
        <MasonryItem src="cld-sample-4" alt="sample image 4" />
        <MasonryItem src="cld-sample-5" alt="sample image 5" />
        <MasonryItem src="cld-sample" alt="sample image 1 again" />
        <MasonryItem src="cld-sample-2" alt="sample image 2 again" />
        <MasonryItem src="cld-sample-3" alt="sample image 3 again" />
      </MasonryGallery>
    </div>
  );
}
Code language: JavaScript (javascript)

I’ve rendered the MasonryGallery component on the page and used multiple MasonryItem components, each pointing to a sample image from Cloudinary. The images are identified by their public IDs, such as cld-sample, cld-sample-2, and so on. These sample images are publicly available, but in an actual project, you’d replace them with your image assets hosted on Cloudinary.

You can customize the images by replacing the src value with the public ID of any image from your Cloudinary account. For example, if you’ve uploaded an image named my-awesome-image, you’d use it like this:

<MasonryItem src="my-awesome-image" alt="My awesome image" />
Code language: HTML, XML (xml)

This approach makes it easy to manage and optimize images hosted on Cloudinary, and they’ll be delivered in the best possible format based on the user’s device.

To ensure the Masonry gallery looks great across different screen sizes, we can add a couple of media queries to adjust the number of columns and the row spans for particular images. Here’s the updated CSS:

@media screen and (min-width: 640px) {
  .masonry-gallery {
    grid-template-columns: repeat(4, 1fr);
  }

  .masonry-item:nth-child(6),
  .masonry-item:nth-child(7) {
    grid-row: span 2;
  }
}

@media screen and (min-width: 1024px) {
  .masonry-gallery {
    grid-template-columns: repeat(5, 1fr);
  }

  .masonry-item:nth-child(6),
  .masonry-item:nth-child(7) {
    grid-row: span 1;
  }
}
Code language: CSS (css)

With this simple addition, the gallery will adapt to different screen sizes.

On small screens (less than 640px wide), the gallery will stack images in two columns.

masonry gallery in a phone device, two columns

On medium screens (640px to 1024px wide), it will display four columns, with the last two images taking up more vertical space.

masonry gallery in a tablet device, four columns

On large screens (1024px and above), it will display five columns, with the last two images back to their normal span.

masonry gallery in a desktop device

And that’s it! We’ve built a responsive Masonry gallery using Next.js, Cloudinary, and Tailwind CSS in just a few steps. This layout is a creative way to display a small collection of images and maximize space.

By leveraging Cloudinary for image optimization and delivery, we ensure that our gallery performs well across different devices. Adding some simple responsive styles with Tailwind ensures the gallery adapts perfectly to any screen size.

Feel free to take this example and tweak it to fit your needs – whether you decide to change the number of columns, using your Cloudinary images, or adding more custom styles. I hope this has inspired you to try out Masonry layouts in your projects and take advantage of how simple it can be to create one with these tools!

If you found this blog post helpful and want to discuss it in more detail, head over to the Cloudinary Community forum and its associated Discord.

Back to top

Featured Post