Masonry Layout
Masonry Layout
Masonry is a JavaScript grid layout library that creates beautiful cascading grid layouts. It works by placing elements in optimal position based on available vertical space, similar to how a mason fits stones in a wall.
Overview
Masonry is perfect for image galleries, portfolios, blog layouts, and any content that has varying heights. It automatically arranges items to minimize gaps and create an aesthetically pleasing layout.
Key Features
- Smart Positioning: Automatically finds the optimal layout for items
- Responsive: Adapts seamlessly to different screen sizes
- Flexible Content: Works with images, cards, or any HTML content
- Percentage-based: Columns based on container size for true responsiveness
- Lightweight: Small file size with no dependencies (except for image support)
- Progressive Enhancement: Works with or without JavaScript
- Dark Mode Compatible: Adapts to your current theme
Installation
Masonry is already included in UltraViolet and ready to use. It's loaded via both CDN (for static pages) and npm (for Vite builds).
CDN (Included)
<!-- JavaScript (in cdn-scripts.blade.php) -->
<script src="https://cdn.jsdelivr.net/npm/masonry-layout@4.2.2/dist/masonry.pkgd.min.js"></script>
NPM (Included)
// Already imported in admin-static.js
import Masonry from 'masonry-layout'
import imagesLoaded from 'imagesloaded'
Important: Using Masonry with Images
⚠️ CRITICAL: When using Masonry with images, you MUST wait for all images to load before initializing Masonry. This is because Masonry needs to know the height of each item to position them correctly.
Why imagesLoaded is Required
When Masonry initializes, it calculates the position of each item based on its height. If images haven't loaded yet, their height is 0, which causes:
- Overlapping items: Items stack on top of each other
- Incorrect layout: Grid doesn't flow properly
- Visual glitches: Items jump around as images load
The imagesLoaded plugin (already included in UltraViolet) solves this by waiting for all images to load before laying out the grid.
Wrong vs Right Way
// ❌ WRONG - Without imagesLoaded (images will overlap!)
const msnry = new Masonry('.grid', {
itemSelector: '.grid-item',
columnWidth: 200
});
// ✅ CORRECT - With imagesLoaded
const msnry = new Masonry('.grid', {
itemSelector: '.grid-item',
columnWidth: 200
});
imagesLoaded('.grid', function() {
msnry.layout();
});
// ✅ BEST - Use our helper function (includes imagesLoaded automatically)
window.initMasonry('.grid', {
itemSelector: '.grid-item',
columnWidth: 200
});
Basic Usage
HTML Structure
Create a container with grid items:
<div class="grid" id="my-masonry-grid">
<!-- Grid sizer for column width calculations -->
<div class="grid-sizer"></div>
<!-- Grid items -->
<div class="grid-item">
<img src="image1.jpg" class="img-fluid" alt="Image 1">
</div>
<div class="grid-item">
<img src="image2.jpg" class="img-fluid" alt="Image 2">
</div>
<div class="grid-item">
<img src="image3.jpg" class="img-fluid" alt="Image 3">
</div>
<!-- Add more items as needed -->
</div>
CSS (Required)
Define the width of your grid items:
/* Grid container */
.grid {
max-width: 1200px;
margin: 0 auto;
}
/* Column width and grid items */
.grid-sizer,
.grid-item {
width: calc(33.333% - 10px);
}
/* Grid items */
.grid-item {
float: left;
margin-bottom: 15px;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.grid-sizer,
.grid-item {
width: calc(50% - 10px);
}
}
@media (max-width: 480px) {
.grid-sizer,
.grid-item {
width: 100%;
}
}
JavaScript Initialization
Option 1: Using UltraViolet's Helper Function (Recommended)
// This automatically includes imagesLoaded support
window.initMasonry('#my-masonry-grid', {
itemSelector: '.grid-item',
columnWidth: '.grid-sizer',
percentPosition: true,
gutter: 15
});
Option 2: Manual Initialization
// Initialize Masonry
const msnry = new Masonry('#my-masonry-grid', {
itemSelector: '.grid-item',
columnWidth: '.grid-sizer',
percentPosition: true,
gutter: 15
});
// CRITICAL: Wait for images to load before laying out
imagesLoaded('#my-masonry-grid', function() {
msnry.layout();
});
Advanced Usage
Mixed Width Items
Create items that span multiple columns:
<div class="grid">
<div class="grid-sizer"></div>
<!-- Regular item (1 column) -->
<div class="grid-item">
<img src="small.jpg" alt="Small">
</div>
<!-- Wide item (2 columns) -->
<div class="grid-item grid-item--width2">
<img src="wide.jpg" alt="Wide">
</div>
<!-- Regular item -->
<div class="grid-item">
<img src="small2.jpg" alt="Small 2">
</div>
</div>
/* Regular items (1 column) */
.grid-sizer,
.grid-item {
width: calc(33.333% - 10px);
}
/* Wide items (2 columns) */
.grid-item--width2 {
width: calc(66.666% - 10px);
}
With Cards
Masonry works great with Bootstrap cards:
<div class="grid">
<div class="grid-sizer"></div>
<div class="grid-item">
<div class="card">
<img src="image1.jpg" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">Card Title</h5>
<p class="card-text">Some quick example text.</p>
</div>
</div>
</div>
<!-- More card items -->
</div>
Dynamic Content
When adding new items dynamically, call layout():
// Initialize grid
const msnry = window.initMasonry('.grid');
// Add new items
const newItem = document.createElement('div');
newItem.className = 'grid-item';
newItem.innerHTML = '<img src="new.jpg" alt="New">';
document.querySelector('.grid').appendChild(newItem);
// IMPORTANT: Wait for new image to load, then re-layout
imagesLoaded(newItem, function() {
msnry.layout();
});
Configuration Options
Core Options
| Option | Type | Default | Description |
|---|---|---|---|
itemSelector |
String | '.grid-item' |
Selector for grid items |
columnWidth |
String/Number | '.grid-sizer' |
Column width (selector or pixels) |
percentPosition |
Boolean | true |
Use percentages for positioning (responsive) |
gutter |
Number | 15 |
Space between items (pixels) |
horizontalOrder |
Boolean | false |
Maintain horizontal left-to-right order |
fitWidth |
Boolean | false |
Set container width to fit columns |
originLeft |
Boolean | true |
Set to false for right-to-left layouts |
originTop |
Boolean | true |
Set to false for bottom-up layouts |
Using percentPosition
For responsive layouts, always use percentPosition: true:
window.initMasonry('.grid', {
percentPosition: true // Makes grid truly responsive
});
This makes Masonry use percentages instead of pixels for positioning, ensuring the layout adapts smoothly to different screen sizes.
Using columnWidth
With Grid Sizer (Recommended):
{
columnWidth: '.grid-sizer', // Use element for calculations
percentPosition: true
}
With Fixed Width:
{
columnWidth: 200, // Fixed 200px columns
percentPosition: false
}
Methods
layout()
Trigger a layout recalculation:
const msnry = window.initMasonry('.grid');
msnry.layout();
destroy()
Clean up and remove Masonry:
const msnry = window.initMasonry('.grid');
// Later, when you're done
window.destroyMasonry(msnry);
Combining with Other Plugins
Masonry + Lightbox
Perfect combination for image galleries:
<div class="grid">
<div class="grid-sizer"></div>
<div class="grid-item">
<a href="full-size.jpg" data-toggle="lightbox" data-gallery="gallery">
<img src="thumbnail.jpg" class="img-fluid" alt="Image">
</a>
</div>
<!-- More items -->
</div>
// Initialize Masonry
window.initMasonry('.grid');
// Initialize Lightbox on all images
document.querySelectorAll('[data-toggle="lightbox"]').forEach(el => {
el.addEventListener('click', Lightbox.initialize);
});
Helper Functions
UltraViolet provides convenient global helper functions:
initMasonry(container, options)
Initialize Masonry with automatic imagesLoaded support:
const msnry = window.initMasonry('#my-grid', {
itemSelector: '.grid-item',
gutter: 20
});
Parameters:
container(String|Element) - Container selector or elementoptions(Object) - Masonry options
Returns: Masonry instance
destroyMasonry(instance)
Clean up a Masonry instance:
window.destroyMasonry(msnry);
Best Practices
1. Always Use imagesLoaded
Never initialize Masonry with images without waiting for them to load:
// ✅ Always use imagesLoaded
window.initMasonry('.grid'); // Our helper includes this
// ❌ Don't do this with images
new Masonry('.grid'); // Will cause overlapping!
2. Use a Grid Sizer
The grid sizer helps Masonry calculate column widths:
<div class="grid">
<div class="grid-sizer"></div> <!-- Always include this -->
<div class="grid-item">...</div>
</div>
3. Set percentPosition: true
For responsive layouts:
{
percentPosition: true // Always use for responsive
}
4. Define Gutters
Add spacing between items:
{
gutter: 15 // Pixels between items
}
5. Call layout() After Changes
When content changes:
// After adding/removing items
msnry.layout();
6. Use Float: Left
Ensure grid items have float: left:
.grid-item {
float: left;
}
7. Responsive CSS
Define breakpoints for different screen sizes:
@media (max-width: 768px) {
.grid-item { width: 50%; }
}
@media (max-width: 480px) {
.grid-item { width: 100%; }
}
Common Issues
Images Overlapping
Problem: Items stack on top of each other.
Solution: Use imagesLoaded:
// ✅ Correct
window.initMasonry('.grid'); // Includes imagesLoaded
// Or manually:
const msnry = new Masonry('.grid');
imagesLoaded('.grid', () => msnry.layout());
Layout Not Responsive
Problem: Grid doesn't adapt to screen size.
Solution: Use percentPosition: true:
{
percentPosition: true,
columnWidth: '.grid-sizer'
}
Items Not Arranging Properly
Problem: Items don't fill gaps correctly.
Solution: Ensure CSS is correct:
.grid-item {
float: left; /* Must have float */
width: 33.333%; /* Set explicit width */
}
Layout Breaks on Resize
Problem: Grid breaks when window resizes.
Solution: Our helper function includes resize handling, or manually:
let resizeTimer;
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
msnry.layout();
}, 250);
});
Troubleshooting
Check Image Loading
Verify images are loading before Masonry initializes:
imagesLoaded('.grid', function() {
console.log('All images loaded!');
});
Verify Grid Structure
Ensure HTML structure is correct:
- Container with class
.grid - Grid sizer:
<div class="grid-sizer"></div> - Items with class
.grid-item - Items have
float: leftin CSS
Test Without Images
If having issues, test with solid color divs first:
<div class="grid-item" style="background: red; height: 200px;"></div>
Live Examples
Visit the Masonry Component Page to see live examples and interactive demonstrations.
Browser Support
Masonry supports all modern browsers:
- ✅ Chrome (latest 2 versions)
- ✅ Firefox (latest 2 versions)
- ✅ Safari (latest 2 versions)
- ✅ Edge (latest 2 versions)
- ✅ Mobile browsers (iOS Safari, Chrome Mobile)
Additional Resources
Summary
Masonry creates beautiful, responsive grid layouts by intelligently arranging items to minimize gaps. When using images:
- Always use imagesLoaded - Critical for proper layout
- Use percentPosition - For responsive layouts
- Include grid-sizer - Helps calculate column widths
- Set gutter - Add spacing between items
- Call layout() - After dynamic changes
UltraViolet makes Masonry easy with the window.initMasonry() helper function that includes imagesLoaded support automatically!