If you’re a UX nerd, you probably love a good staggered grid view. It’s super common in mobile development but kinda overlooked on the web — maybe because it’s such a hassle to implement properly. Even though the experimental stuff is ongoing (you can check it out here: masonry-update), most people still avoid it.
Personally, I love this kind of grid because once you have it in your app, your app stops annoying users by limiting the picture size they can upload. Users can throw in whatever weird aspect ratio they want and the grid will still render beautifully. Basically looks like Pinterest.
In this post, I’m writing the minimum setup for using the masonry-layout package by David DeSandro in Svelte 5 runes mode. The whole component is around 35 lines — nothing crazy.
Packages
Before jumping into the Svelte part, you gotta install the Masonry package first:
npm i -D masonry-layout
And if you’re using TypeScript (which I am), also install the types:
npm i -D @types/masonry-layout
Component
Here’s the code snippet from my MasonryLayout.svelte component, plus the usage example below it.
<script lang="ts">
import { onMount } from 'svelte'
let { items }: { items: string[] } = $props()
let msnryEl = $state<HTMLElement | null>(null)
let msnryWidth = $state(0)
// 2 columns on mobile, 3 on larger screens
let columnWidth = $derived(msnryWidth < 640 ? msnryWidth / 2 : msnryWidth / 3)
const initMsnryWidth = () => {
const rect = msnryEl?.getBoundingClientRect()
msnryWidth = rect?.width || 0
}
const initMasonryLayout = async () => {
const Masonry = (await import('masonry-layout')).default
new Masonry(msnryEl!, {
itemSelector: '.grid-item',
columnWidth: columnWidth,
horizontalOrder: true
})
}
const onResize = () => {}
onMount(() => {
if (msnryEl) {
initMsnryWidth()
initMasonryLayout()
}
})
</script>
<svelte:window onresize={onResize} />
<ul class="w-full" bind:this={msnryEl}>
{#each items as item}
<li class="grid-item p-1" style={`width: ${columnWidth}px;`}>
<img src={item} alt="" />
</li>
{/each}
</ul>
Usage
And here’s how you use MasonryLayout.svelte in your Svelte page:
<script lang="ts">
import MasonryLayout from '$lib/components/MasonryLayout.svelte'
let images = [
'https://images.pexels.com/photos/34533776/pexels-photo-34533776.jpeg',
'https://images.pexels.com/photos/33773909/pexels-photo-33773909.jpeg',
'https://images.pexels.com/photos/31740993/pexels-photo-31740993.jpeg',
'https://images.pexels.com/photos/29531010/pexels-photo-29531010.jpeg',
'https://images.pexels.com/photos/17567462/pexels-photo-17567462.jpeg',
'https://images.pexels.com/photos/34642333/pexels-photo-34642333.jpeg',
'https://images.pexels.com/photos/34517503/pexels-photo-34517503.png'
]
</script>
<div class="m-4">
<MasonryLayout items={images} />
</div>
That’s all for this one — see ya!
