Masonry Grid Layout: Complete Guide with CSS & React (3 Approaches)

Cover Image for Masonry Grid Layout: Complete Guide with CSS & React (3 Approaches)
toofancoder
toofancoder

What is a Masonry Grid Layout?

A masonry grid layout is a type of grid design where items are placed in columns but without uniform row heights — each item slots into the next available vertical space, much like bricks in a wall. Think Pinterest, Google Images, or Unsplash. It's one of the most popular layout patterns for image galleries, card feeds, and photo grids.

In this guide, you'll learn 3 practical approaches to build a masonry style grid:

  1. Pure CSS using the columns property — no dependencies, works today
  2. CSS Grid with grid-template-rows: masonry — the native future
  3. React using react-stack-grid — for dynamic content in Next.js

Let's dive in.


Approach 1: Pure CSS Masonry Grid (CSS Columns)

This is the simplest and most browser-compatible approach. It uses the CSS columns property to create a masonry style layout with zero JavaScript.

How it works

The columns property splits the container into N equal-width columns. Items flow naturally from top to bottom within each column, which gives you the masonry effect automatically.

.masonry-grid {
  columns: 3;
  column-gap: 16px;
}

.masonry-grid-item {
  break-inside: avoid;
  margin-bottom: 16px;
}
<div class="masonry-grid">
  <div class="masonry-grid-item"><img src="photo1.jpg" alt="photo 1" /></div>
  <div class="masonry-grid-item"><img src="photo2.jpg" alt="photo 2" /></div>
  <div class="masonry-grid-item"><img src="photo3.jpg" alt="photo 3" /></div>
  <div class="masonry-grid-item"><img src="photo4.jpg" alt="photo 4" /></div>
  <div class="masonry-grid-item"><img src="photo5.jpg" alt="photo 5" /></div>
</div>

The key rule is break-inside: avoid on each item — this prevents an image or card from being split across two columns.

Making it responsive

Use column-count with media queries, or use the columns shorthand with a min-width hint so the browser decides how many columns fit:

.masonry-grid {
  columns: 3 280px; /* at most 3 columns, min 280px each */
  column-gap: 16px;
}

@media (max-width: 768px) {
  .masonry-grid {
    columns: 2;
  }
}

@media (max-width: 480px) {
  .masonry-grid {
    columns: 1;
  }
}

Pros and Cons

CSS Columns
No JavaScript or libraries needed
Excellent browser support
Works with any content type
Items flow top-to-bottom (not left-to-right)
Hard to control item ordering

Approach 2: CSS Grid Masonry (The Native Future)

CSS Grid Level 3 introduces grid-template-rows: masonry — a native masonry layout built directly into the browser. This is the cleanest solution but is currently only available in Firefox behind a flag (Chrome support is still in progress).

.masonry-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: masonry;
  gap: 16px;
}
<div class="masonry-grid">
  <div><img src="photo1.jpg" alt="masonry photo grid image 1" /></div>
  <div><img src="photo2.jpg" alt="masonry photo grid image 2" /></div>
  <div><img src="photo3.jpg" alt="masonry photo grid image 3" /></div>
</div>

This is the ideal masonry grid design for the future — items are placed left-to-right (unlike CSS columns) and row gaps are handled automatically by the browser. Keep an eye on browser support and adopt this when it becomes widely available.


Approach 3: React Masonry Grid with Next.js

For dynamic content in React or Next.js (like a photo feed loaded from an API), a JavaScript-based approach handles image loading and layout re-calculation properly.

We'll use react-stack-grid — a lightweight React component for masonry style grid layouts.

Step 1: Install the library

npm install react-stack-grid

Step 2: Create the masonry photo grid component

Create a MasonryGallery.jsx component:

'use client';

import { useEffect, useState } from 'react';
import StackGrid from 'react-stack-grid';

const PHOTOS = [
  'https://picsum.photos/seed/1/400/600',
  'https://picsum.photos/seed/2/400/300',
  'https://picsum.photos/seed/3/400/500',
  'https://picsum.photos/seed/4/400/400',
  'https://picsum.photos/seed/5/400/700',
  'https://picsum.photos/seed/6/400/350',
];

export default function MasonryGallery() {
  const [grid, setGrid] = useState(null);

  useEffect(() => {
    // Re-calculate layout once images have loaded
    const timer = setTimeout(() => {
      if (grid) grid.updateLayout();
    }, 1000);
    return () => clearTimeout(timer);
  }, [grid]);

  return (
    <StackGrid
      columnWidth="30%"
      duration={0}
      gutterWidth={16}
      gutterHeight={16}
      gridRef={(g) => setGrid(g)}
    >
      {PHOTOS.map((src, index) => (
        <div key={index}>
          <img
            src={src}
            alt={`Masonry photo grid image ${index + 1}`}
            style={{ width: '100%', display: 'block', borderRadius: '8px' }}
          />
        </div>
      ))}
    </StackGrid>
  );
}

Step 3: Use it in your Next.js page

// pages/gallery.js or app/gallery/page.jsx
import MasonryGallery from '@/components/MasonryGallery';

export default function GalleryPage() {
  return (
    <main>
      <h1>My Masonry Photo Grid</h1>
      <MasonryGallery />
    </main>
  );
}

Configuring column widths responsively

react-stack-grid accepts a columnWidth prop that can be a percentage or a fixed pixel value. Here's how to make it adapt to screen size:

const [columnWidth, setColumnWidth] = useState('30%');

useEffect(() => {
  const handleResize = () => {
    if (window.innerWidth < 480) setColumnWidth('100%');
    else if (window.innerWidth < 768) setColumnWidth('45%');
    else setColumnWidth('30%');
  };
  handleResize();
  window.addEventListener('resize', handleResize);
  return () => window.removeEventListener('resize', handleResize);
}, []);

return (
  <StackGrid columnWidth={columnWidth} gutterWidth={16} gutterHeight={16}>
    {/* items */}
  </StackGrid>
);

Pros and Cons

react-stack-grid
Items flow left-to-right (natural reading order)
Handles dynamic content and image load events
Animated transitions between layout changes
Adds a JavaScript dependency
Requires updateLayout() workaround for images

Which Approach Should You Use?

Use case Best approach
Static photo gallery, blog, or portfolio CSS Columns
You want left-to-right ordering with no JS CSS Grid Masonry (when available)
Dynamic content from API in React/Next.js react-stack-grid
Pinterest-style feed with animations react-stack-grid

Common Masonry Grid Design Tips

1. Use consistent gutter spacing A 16px gap (1rem) between items is a safe default. Increase to 24px for content-heavy grids.

2. Don't mix aspect ratios on small screens On mobile (1 column), masonry has no visual effect — make sure your images look good at full width.

3. Always set width: 100% on images inside grid items Without this, images won't fill the column and your masonry layout will break.

.masonry-grid-item img {
  width: 100%;
  display: block; /* removes bottom whitespace gap */
  border-radius: 8px;
}

4. Lazy-load images in large grids For masonry photo grids with 50+ images, add loading="lazy" to your <img> tags or use the Next.js <Image> component:

import Image from 'next/image';

<Image
  src={src}
  alt="masonry grid image"
  width={400}
  height={300}
  loading="lazy"
/>

Conclusion

A masonry style grid layout is one of the most visually satisfying patterns in web design — and it's more achievable than most developers think.

Here's a quick recap:

  • CSS Columns — the easiest and most compatible approach, great for static content
  • CSS Grid Masonry — the native future, perfect once browser support improves
  • react-stack-grid — the best option for dynamic React/Next.js apps with image galleries

Pick the approach that matches your project's needs, and don't forget to always test your masonry grid design on mobile!

If you're building a Next.js project and want a full working example, check out the react-stack-grid demo on GitHub.

Happy coding! 🚀