Masonry Grid Layout: Complete Guide with CSS & React (3 Approaches)
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:
- Pure CSS using the
columnsproperty — no dependencies, works today - CSS Grid with
grid-template-rows: masonry— the native future - 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! 🚀