Stackbits
Components > Masonry Grid

Masonry Grid

Masonry Grid is the perfect solution for dynamic, Pinterest-style layouts that adapt beautifully to any screen size. Built with React, Next.js, and Tailwind CSS, this grid intelligently arranges elements into an optimized, gap-free layout, making it ideal for image galleries, portfolios, and product listings. Say goodbye to rigid rows and columns—Masonry Grid ensures your content flows seamlessly, no matter the shape or size. Fully responsive, lightweight, and easy to customize, this component helps you create visually stunning layouts without the hassle. Get started with StackBits and elevate your UI with effortless grid magic! 🚀

Preview

Urban Skyline
Mountain Retreat
Forest Wander
Serene Lake
Golden Hour
Coastal Vibes
Night Lights
Rustic Charm
Blooming Meadows
Mystic Mountains
Abstract Art
Desert Mirage
City Buzz
Urban Skyline
Mountain Retreat
Forest Wander
City Buzz
Quiet Alley
Vibrant Street
Quiet Alley
Vibrant Street
Calm Waters
Snowy Peaks
Sunset Bliss
Cultural Heritage
Modern Minimalism
Calm Waters
Snowy Peaks
Sunset Bliss
Cultural Heritage
Modern Minimalism
Serene Lake
Golden Hour
Coastal Vibes
Night Lights
Rustic Charm
Blooming Meadows
Mystic Mountains
Abstract Art
Desert Mirage

Follow below steps 👇🏻

Install dependencies

1
npm i framer-motion tailwindcss-animate

Component

Create a file masonry-grid.tsx in your components folder and paste this code

1
'use client';
2
3
import React, { useState } from 'react';
4
import { motion } from 'framer-motion';
5
import Image from 'next/image';
6
7
interface MasonryGridProps {
8
items: { image: string; title: string; description: string }[];
9
columns?: number | undefined;
10
}
11
12
const MasonryGrid: React.FC<MasonryGridProps> = ({ items, columns = undefined }) => {
13
const [imagesLoaded, setImagesLoaded] = useState<{ [key: string]: boolean }>({});
14
15
if (!items || items.length === 0) {
16
return <div className="text-center p-4">No items to display</div>;
17
}
18
19
return (
20
<div
21
style={{
22
columns: columns
23
}}
24
className={`${
25
!columns && 'columns-1 sm:columns-2 md:columns-3 lg:columns-4'
26
} gap-2 overflow-y-auto p-3 w-full`}
27
>
28
{items.map((item, index) => {
29
// Calculate row number based on screen size and index
30
const getColumnCount = () => {
31
if (typeof window === 'undefined') return 1;
32
const width = window.innerWidth;
33
if (width >= 1024) return 4; // lg
34
if (width >= 768) return 3; // md
35
if (width >= 640) return 2; // sm
36
return 1;
37
};
38
39
const columnCount = getColumnCount();
40
const rowIndex = Math.floor(index / columnCount);
41
42
return (
43
<motion.div
44
key={index}
45
className="break-inside-avoid mb-4 relative group rounded-xl overflow-hidden p-1 border-[1px] border-transparent hover:border-[1px] hover:border-neutral-800"
46
initial={{ opacity: 0, y: 20 }}
47
animate={{
48
opacity: 1,
49
y: 0,
50
transition: {
51
duration: 0.5,
52
delay: rowIndex * 0.1,
53
ease: 'easeOut'
54
}
55
}}
56
whileHover={{ scale: 1.05, rotateZ: 1 }}
57
>
58
<div className="relative">
59
<div className="relative w-full flex gap-1 flex-col items-start justify-start">
60
{!imagesLoaded[item.image] && (
61
<div className="absolute inset-0 w-full h-[300px] bg-neutral-500/50 animate-pulse rounded-lg" />
62
)}
63
<Image
64
src={item.image}
65
alt={item.title}
66
width={400}
67
height={300}
68
className={`${
69
!imagesLoaded[item.image] ? 'opacity-0' : 'opacity-100'
70
} w-full h-auto transition-transform duration-300 rounded-lg`}
71
sizes="(max-width: 640px) 100vw, (max-width: 768px) 50vw, (max-width: 1024px) 33vw, 25vw"
72
onLoad={() => {
73
setImagesLoaded((prev) => ({ ...prev, [item.image]: true }));
74
}}
75
onError={() => {
76
console.error(`Error loading image: ${item.image}`);
77
setImagesLoaded((prev) => ({ ...prev, [item.image]: true }));
78
}}
79
/>
80
{imagesLoaded[item.image] && (
81
<div className="w-full">
82
<h3 className="text-sm font-medium">{item.title}</h3>
83
<p className="mt-0 text-xs text-neutral-500 line-clamp-2 overflow-hidden">
84
{item.description}
85
</p>
86
</div>
87
)}
88
</div>
89
</div>
90
</motion.div>
91
);
92
})}
93
</div>
94
);
95
};
96
97
export default MasonryGrid;

Usage

1
<MasonryGrid
2
items={[
3
{
4
title: 'Urban Skyline',
5
image:
6
'https://images.unsplash.com/photo-1718563552473-2d97b224e801?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHw1fHx8ZW58MHx8fHx8',
7
description:
8
'A breathtaking view of a modern cityscape with towering skyscrapers illuminated at dusk.'
9
},
10
{
11
title: 'Mountain Retreat',
12
image:
13
'https://images.unsplash.com/photo-1735317461815-1a0ba64e9a56?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHwyMXx8fGVufDB8fHx8fA%3D%3D',
14
description:
15
'A serene cabin nestled in the heart of towering mountains, perfect for a peaceful getaway.'
16
},
17
{
18
title: 'Forest Wander',
19
image:
20
'https://images.unsplash.com/photo-1502082553048-f009c37129b9?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
21
description:
22
'A misty trail winding through a dense, enchanting forest filled with lush greenery.'
23
},
24
{
25
title: 'Serene Lake',
26
image:
27
'https://images.unsplash.com/photo-1504384308090-c894fdcc538d?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
28
description:
29
'A tranquil lake reflecting the golden hues of the sunset, surrounded by peaceful nature.'
30
},
31
{
32
title: 'Golden Hour',
33
image:
34
'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
35
description:
36
'A mesmerizing sunset casting a warm glow over the ocean, creating a dreamlike atmosphere.'
37
},
38
{
39
title: 'Coastal Vibes',
40
image:
41
'https://images.unsplash.com/photo-1493558103817-58b2924bce98?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
42
description:
43
'Crystal-clear waves crashing against a sandy shore, offering a perfect beach escape.'
44
},
45
{
46
title: 'Night Lights',
47
image:
48
'https://images.unsplash.com/photo-1502933691298-84fc14542831?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Mnx8d2F0ZXIlMjBzcG9ydHxlbnwwfHwwfHx8MA%3D%3D',
49
description:
50
'A dazzling city skyline at night, with vibrant lights illuminating the urban landscape.'
51
},
52
{
53
title: 'Rustic Charm',
54
image:
55
'https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
56
description:
57
'A cozy wooden cabin with a warm, inviting atmosphere set in a countryside setting.'
58
}
59
]}
60
></MasonryGrid>

⭐️ Got a question or feedback?
Feel free to reach out!