Documentation

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 element
  • options (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:

  1. Container with class .grid
  2. Grid sizer: <div class="grid-sizer"></div>
  3. Items with class .grid-item
  4. Items have float: left in 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:

  1. Always use imagesLoaded - Critical for proper layout
  2. Use percentPosition - For responsive layouts
  3. Include grid-sizer - Helps calculate column widths
  4. Set gutter - Add spacing between items
  5. Call layout() - After dynamic changes

UltraViolet makes Masonry easy with the window.initMasonry() helper function that includes imagesLoaded support automatically!