Pinterest Grid Without a JavaScript Library
Building a masonry layout with Tailwind CSS is easier than you might think: CSS columns solves 80% of cases without a single line of JavaScript. For the remaining 20%, row sorting, dynamic content and filtering, Alpine.js provides the logic you need without external dependencies.
Table of Contents
- 1. Why masonry, and when it makes sense
- 2. CSS columns: the native masonry solution
- 3. CSS columns with Tailwind classes
- 4. Gap, breakpoints and responsive column count
- 5. CSS Grid masonry: the coming standard
- 6. Alpine.js masonry for complete layout control
- 7. Image masonry with lazy loading and aspect ratios
- 8. CSS columns vs. Grid masonry vs. Alpine.js: comparison
- 9. Performance optimization for large masonry grids
- 10. Summary
- 11. FAQ
1. Why masonry, and when it makes sense
A masonry layout is a grid layout in which items of different heights are arranged into columns without vertical gaps. The items in each column follow directly on from one another, without being forced by the grid onto a shared row height. The result is a dense, visually efficient presentation of heterogeneous content, the kind you know from Pinterest, Unsplash and countless image galleries. The advantage over a classic grid: there are no white gaps beneath shorter cards sitting next to taller ones.
A Tailwind masonry layout makes sense for image galleries with varying aspect ratios, blog card grids with variable text heights, product grids in e-commerce (especially for clothing and accessories), Pinterest-style content discovery pages, and portfolio pages with mixed media formats. Masonry is not suited, however, to tabular data, where horizontal alignment across rows matters, or to content where the order of items carries a semantic meaning that column-wise reading would disrupt.
2. CSS columns: the native masonry solution
The simplest way to implement a masonry layout is the CSS columns property. This property splits a container into multiple columns and automatically distributes the child elements across them, so that each column ends up roughly the same height. That is not true masonry in the technical sense (items fill each column from top to bottom, they are not sorted by height), but the result looks identical and has the same visual effect.
The decisive advantage of CSS columns for a Tailwind masonry layout: it works without JavaScript, without external libraries and without a build step. Browser support is complete (every browser since 2011). The only technical downside: items are ordered column by column (first all items in column 1, then column 2, and so on), not row by row (all items in the first row, then the second). For image galleries, where the visual arrangement matters more than the semantic order, that is not a problem. For numbered lists or chronologically ordered blog posts it can be undesirable.
<!-- CSS columns Masonry with Tailwind, no JavaScript required -->
<div class="columns-1 sm:columns-2 lg:columns-3 xl:columns-4 gap-4 p-4">
<!-- Each card uses break-inside-avoid to prevent column breaks mid-card -->
<div class="break-inside-avoid mb-4 rounded-2xl overflow-hidden bg-white
shadow-sm border border-slate-200">
<img src="/img/photo-1.jpg" alt="Image 1"
class="w-full h-auto object-cover"
loading="lazy">
<div class="p-4">
<h3 class="font-semibold text-slate-800 text-sm">Image title 1</h3>
<p class="text-slate-500 text-xs mt-1">Short description of the image content</p>
</div>
</div>
<div class="break-inside-avoid mb-4 rounded-2xl overflow-hidden bg-white
shadow-sm border border-slate-200">
<!-- Taller card, Masonry fills the gap naturally -->
<img src="/img/photo-2.jpg" alt="Image 2"
class="w-full h-auto object-cover"
loading="lazy">
<div class="p-4">
<h3 class="font-semibold text-slate-800 text-sm">Image title 2</h3>
<p class="text-slate-500 text-xs mt-1">
A slightly longer description that makes the card taller and so
demonstrates the masonry effect.
</p>
<div class="mt-3 flex gap-2">
<span class="px-2 py-0.5 bg-sky-100 text-sky-700 text-xs rounded-full">Tag 1</span>
<span class="px-2 py-0.5 bg-slate-100 text-slate-600 text-xs rounded-full">Tag 2</span>
</div>
</div>
</div>
<!-- More cards follow, columns distributes them automatically -->
</div>
3. CSS columns with Tailwind classes
For the masonry layout via CSS columns, Tailwind CSS offers several direct utility classes. The columns-{n} classes set a fixed column count: columns-2 for two columns, columns-3 for three. The columns-{size} classes set a minimum column width: columns-sm (8rem), columns-md (16rem), columns-lg (24rem). With the width variant, the browser automatically adapts the column count to the available space, similar to auto-fill in CSS Grid.
The most important Tailwind class for masonry layouts with CSS columns is break-inside-avoid. It sets break-inside: avoid on every child element and stops the browser from breaking an element right in the middle between two columns, which without this class leads to visible cut-off artifacts on cards with a border, background or shadow. break-inside-avoid is the one essential technical detail of the CSS columns masonry approach; everything else is layout and styling.
4. Gap, breakpoints and responsive column count
The gap between columns and the gap between items are among the most important design parameters of a Tailwind masonry layout. With CSS columns there is a technical distinction: the gap-* class only sets the horizontal spacing between columns (column-gap), not the vertical spacing between items. The vertical spacing is set as a margin on the child element: mb-4 at the end of each card provides the vertical gap to the next card in the same column.
The responsive column count is the core of a good masonry layout with Tailwind. The standard pattern: columns-1 sm:columns-2 lg:columns-3 xl:columns-4 adapts the column count step by step to screen width. For very large screens and wide cards, 2xl:columns-5 can make sense. Alternatively, the columns-xs through columns-3xl classes with minimum column width let the browser determine the count automatically, which behaves similarly to responsive containers with auto-fill and is often the better choice for galleries with many variable items.