3 Quick Fixes to Ensure Proper Bootstrap Masonry Image Loading on First Page Visits

Grigor Grantovich Sargsyan
3 min readMay 2, 2023

--

Bootstrap 5 and Masonry JavaScript Library

While creating my own personal website using JavaScript and Bootstrap, I ran into an issue of displaying content cards under my project section. Using a simple “row” class from Bootstrap’s Grid system, I created column blocks where every column had the same height as the tallest card in the same horizontal axis.

This wouldn’t be an issue if all my cards had the same dimensions, but the length of my card contents are variable. My solution was to use the Masonry library for a cascading grid layout as recommended in the Bootstrap documentation. It worked great, expect for one small problem. The first page load problem that comes with images and Masonry.

<div class="row grid" data-masonry='{"percentPosition": true }'>

Masonry works by “placing elements in optimal position based on available vertical space, sort of like a mason fitting stones in a wall.” The issue here arises when Masonry tries to place the cards in these optimal positions before the images inside the content cards load. This can lead to cards overlapping because the height of the divs are determined before the height of the images are known. After the first page load, the images get cached by the browser and this issue no longer happens, but this doesn’t make for a good experience for people who visit a website dedicated to highlighting my projects and experiences.

The solution?

I spent an embarrassing amount of time trying to pinpoint and solve the issue I was facing and I was not alone. There are many other posts scattered across forums asking for proper solutions. After trying almost all of them I decided to consolidate my learnings all into this one article to save future Masonry users the trouble of tracking them all down.

Solution 1: Promise.all( )

This was the solution recommended by the Masonry team themselves and the one I used to fix the first load problem for my personal website. Add a script tag to the end of your html file that uses Promise.all() to await for all the images in your html element to finish rendering and only then apply Masonry. Similar solutions can also be achieved without having to use promises as demonstrated in the first half of this article. This is a good solution if you don’t have very many image assets to await on like me, but it could be a problem if you do have many images.

Promise.all(Array.from(document.images)
.filter(img => !img.complete)
.map(img => new Promise(resolve => { img.onload = img.onerror = resolve; })))
.then(() => {
var msnry = new Masonry('.grid');
msnry.layout();
});

Solution 2: imagesLoaded Library

If you do have a lot of image assets, then it might be worth while to use the imagesLoaded library, which is a JavaScript library created by the same team that created Masonry. Simply import he CDN script or install it using a package manager and you’re ready to go. The documentation also has great examples of how to use imagesLoaded in vanilla JavaScript and jQuery. A Stack Overflow post from 2021 did a great job at explaining how to fix the Masonry error using imagesLoaded and can currently be seen being used by the person who created the post on their personal website when you hit inspect.

$('#portfolio-section').imagesLoaded( function() {
$('.grid').masonry({
itemSelector: '.portfolio-item',
percentPosition: true
})
});

Solution 3: Use Sync

A third solution that has been floated around by the community is to change the script tag that Bootstrap recommends in its documentation at the end from async to sync. The idea here is that if the Masonry library stops running asynchronously with the rest of the html, then the image assets will have time to load first. I wasn’t able to replicate this solution, but it was put forward and endorsed by another Bootstrap user in an issue opened on the Masonry GitHub here.

<script src="https://cdn.jsdelivr.net/npm/masonry-layout@4.2.2/dist/masonry.pkgd.min.js" integrity="sha384-GNFwBvfVxBkLMJpYMOABq3c+d3KnQxudP/mGPkzpZSTYykLBNsZEnG2D9G/X/+7D" crossorigin="anonymous" sync></script>

Hopefully one of these 3 solutions worked for you and saved you all the time, trouble, and energy I spent tracking them down. If you have a solution of your own that wasn’t covered then please leave a comment below explaining it and if you are interested in seeing what my personal website site looks like now you can view it here. Thank you for reading and happy coding!

--

--

Grigor Grantovich Sargsyan

Software Engineering | Data Science | Economics | Storytelling | World History | MMA