Skip to content

Virtuoso Masonry

npm version npm downloads license TypeScript

A virtualized masonry layout component for React that arranges items in a grid with varying heights.

  • Virtualized rendering - only renders visible items for optimal performance with large datasets
  • Variable item heights - items can have different heights, automatically measured and arranged
  • Dynamic column count - change column count based on screen/container width
  • Window scroll support - can use the window as the scroll container
  • Just-in-time distribution - items are distributed to columns as you scroll
bash
import { VirtuosoMasonry } from '@virtuoso.dev/masonry'
import { useMemo } from 'react'

const ItemContent: React.FC<{ data: number }> = ({ data }) => {
  const height = data % 10 === 0 ? 200 : data % 5 === 0 ? 180 : data % 7 ? 150 : 120
  return (
    <div style={{ padding: '5px' }}>
      <div style={{ height, border: '1px solid black' }}>Item {data}</div>
    </div>
  )
}

export default function App() {
  const data = useMemo(() => {
    return Array.from({ length: 1000 }, (_, index) => index)
  }, [])

  return (
    <div>
      <VirtuosoMasonry columnCount={3} data={data} style={{ height: 500 }} initialItemCount={50} ItemContent={ItemContent} />
    </div>
  )
}
import { VirtuosoMasonry } from '@virtuoso.dev/masonry'
import { useEffect, useMemo, useState } from 'react'

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth)
  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth)
    window.addEventListener('resize', handleResize)
    return () => window.removeEventListener('resize', handleResize)
  }, [])
  return width
}

const ItemContent: React.FC<{ data: number }> = ({ data }) => {
  const height = data % 10 === 0 ? 200 : data % 5 === 0 ? 180 : data % 7 ? 150 : 120
  return (
    <div style={{ padding: '5px' }}>
      <div style={{ height, border: '1px solid black' }}>Item {data}</div>
    </div>
  )
}

export default function App() {
  const data = useMemo(() => {
    return Array.from({ length: 1000 }, (_, index) => index)
  }, [])

  const width = useWindowWidth()

  const columnCount = useMemo(() => {
    if (width < 500) {
      return 2
    }
    if (width < 800) {
      return 3
    }
    return 4
  }, [width])

  return (
    <div>
      <VirtuosoMasonry columnCount={columnCount} data={data} useWindowScroll={true} initialItemCount={50} ItemContent={ItemContent} />
    </div>
  )
}