smart-masonry-grid
v0.2.1
Published
A zero-dependency, virtualized masonry grid layout library
Maintainers
Readme
Highlights
- Zero dependencies — uses native
ResizeObserverandIntersectionObserver - Virtualization — renders only visible items, handles 10,000+ items smoothly
- Responsive — auto columns, fixed count, named breakpoints, or custom pixel breakpoints
- SSR-compatible — CSS columns fallback with hydration support for Next.js
- TypeScript-first — full type definitions included
- Dual API — vanilla
MasonryGridclass + React components (<Masonry>,<VirtualMasonry>,useMasonryGrid) - Animations — built-in entry animations with configurable duration, easing, and offset
- Infinite scroll — built-in
onReachEndcallback - Tree-shakeable — ESM + CommonJS,
sideEffects: false
Why smart-masonry-grid?
Most masonry libraries are abandoned, React-only, or missing critical features. Here's how smart-masonry-grid compares:
| Feature | smart-masonry-grid | masonry-layout | react-masonry-css | masonic | @mui/lab Masonry | |---|:---:|:---:|:---:|:---:|:---:| | Zero dependencies | Yes | No | Yes | No | No (heavy) | | Virtualization (10K+ items) | Yes | No | No | Yes | No | | SSR / Next.js support | Yes | No | No | Partial | Buggy | | Vanilla JS + React | Yes | Vanilla only | React only | React only | React only | | Built-in animations | Yes | No | No | No | No | | Built-in infinite scroll | Yes | No | No | Yes | No | | TypeScript (built-in) | Yes | No (@types) | Partial | Yes | Yes | | Responsive breakpoints | 4 modes | No | Basic | No | Basic | | Actively maintained | Yes | Barely | No (4yr+) | Partial | Yes |
No other library combines zero-dependency virtualization, SSR support, and multi-framework support in one package.
Install
npm install smart-masonry-grid# or
yarn add smart-masonry-grid
# or
pnpm add smart-masonry-gridQuick Start
React
<Masonry> — renders all children
import { Masonry } from 'smart-masonry-grid/react';
function Gallery({ photos }) {
return (
<Masonry columns={{ sm: 2, md: 3, lg: 4 }} gap={16} animate>
{photos.map((photo) => (
<img key={photo.id} src={photo.src} alt={photo.alt} />
))}
</Masonry>
);
}<VirtualMasonry> — for large datasets (1,000+ items)
import { VirtualMasonry } from 'smart-masonry-grid/react';
function Gallery({ photos }) {
return (
<VirtualMasonry
totalItems={photos.length}
renderItem={(index) => (
<img src={photos[index].src} alt={photos[index].alt} />
)}
height={600}
columns={4}
gap={16}
animate
/>
);
}useMasonryGrid hook — full control
import { useMasonryGrid } from 'smart-masonry-grid/react';
function CustomGrid({ items }) {
const { containerRef, layout, getItemStyle } = useMasonryGrid({
columns: 3,
gap: 16,
});
return (
<div ref={containerRef} style={{ position: 'relative', height: layout?.totalHeight }}>
{items.map((item, i) => (
<div key={item.id} style={getItemStyle(i)}>
{item.content}
</div>
))}
</div>
);
}Vanilla JS
import { MasonryGrid } from 'smart-masonry-grid';
const grid = new MasonryGrid(document.getElementById('grid'), {
columns: { type: 'auto', minColumnWidth: 250 },
gap: 16,
});
// Dynamic operations
grid.append(newElement);
grid.prepend(newElement);
grid.remove(element);
grid.refresh();
// Events
grid.on('layout', (output) => console.log(output.totalHeight));
grid.on('resize', (width, cols) => console.log(width, cols));
// Cleanup
grid.destroy();Virtualized (vanilla)
const grid = new MasonryGrid(container, {
totalItems: 10000,
renderItem: (index) => {
const el = document.createElement('div');
el.textContent = `Item ${index}`;
return el;
},
});Column Strategies
4 flexible ways to configure columns:
// 1. Fixed column count
<Masonry columns={4} />
// 2. Auto — fill based on minimum column width
<Masonry columns={{ type: 'auto', minColumnWidth: 250 }} />
// 3. Named breakpoints (sm=640, md=768, lg=1024, xl=1280)
<Masonry columns={{ sm: 2, md: 3, lg: 4, xl: 5 }} />
// 4. Custom pixel breakpoints
<Masonry columns={{ 480: 2, 768: 3, 1200: 4 }} />API Reference
<Masonry> Props
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | Items to lay out |
| columns | number \| ColumnStrategy \| NamedBreakpoints | auto, 250px | Column configuration |
| gap | number | 16 | Gap between items in pixels |
| animate | boolean \| AnimationConfig | false | Entry animations |
| onLayout | (output: LayoutOutput) => void | — | Called after each layout computation |
| onReachEnd | () => void | — | Called when scrolled near the bottom (infinite scroll) |
| reachEndThreshold | number | 200 | Pixels from bottom to trigger onReachEnd |
<VirtualMasonry> Props
All <Masonry> props except children, plus:
| Prop | Type | Default | Description |
|---|---|---|---|
| totalItems | number | — | Total number of items |
| renderItem | (index: number) => ReactElement | — | Render function for each item |
| height | number | — | Scroll container height in pixels |
| overscan | number | 600 | Pre-render buffer in pixels |
| estimatedItemHeight | number | 300 | Height estimate for unmeasured items |
| placeholder | ReactElement | — | Shown while an item is being measured |
MasonryGrid (Vanilla JS)
const grid = new MasonryGrid(container: HTMLElement, options?: MasonryGridOptions);| Option | Type | Default | Description |
|---|---|---|---|
| columns | number \| ColumnStrategy | auto, 250px | Column configuration |
| gap | number | 16 | Gap between items in pixels |
| virtualize | boolean | true | Enable virtualization |
| totalItems | number | — | Total items (virtualized mode) |
| renderItem | (index: number) => HTMLElement | — | Item renderer (virtualized mode) |
| ssrFallback | boolean | false | Use CSS columns for SSR |
| resizeDebounceMs | number | 100 | Debounce delay for resize events |
Methods:
| Method | Description |
|---|---|
| append(elements) | Add elements to the end |
| prepend(elements) | Add elements to the beginning |
| remove(id) | Remove an item by ID |
| refresh() | Force a full relayout |
| getLayout() | Returns current LayoutOutput |
| getColumnCount() | Returns current column count |
| on(event, callback) | Subscribe to events |
| destroy() | Clean up observers and DOM |
Events: layout, resize, scroll, itemResize, destroy
SSR
import { getSSRStyles } from 'smart-masonry-grid';
// Returns a CSS string for server-side rendering
const css = getSSRStyles();The library provides a CSS columns fallback for the initial server render, then smoothly transitions to the JS-powered masonry layout after hydration.
Browser Support
Uses native browser APIs with excellent coverage:
| API | Support |
|---|---|
| ResizeObserver | 99%+ |
| IntersectionObserver | 98%+ |
No polyfills needed for modern browsers.
License
MIT — Built by Sounak Das
