Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

masonic

Package Overview
Dependencies
Maintainers
1
Versions
96
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

masonic

An autosizing masonry component which only renders items currently visible in the window.

  • 2.1.10
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
24K
increased by10.21%
Maintainers
1
Weekly downloads
 
Created
Source

🧱 masonic

Bundlephobia Types Build status NPM Version MIT License

npm i masonic

A virtualized masonry grid component for React based on Brian Vaughn's react-virtualized and further inspired by react-window.

Features

  • Easy to use It only takes two minutes to start creating your own masonry grid with this component. For reals, check out the demo on CodeSandbox.
  • Blazing™ fast This component can seamlessly render hundreds of thousands of grid items without issue via virtualization and intelligent data structures. It uses a red black interval tree to determine which grid items with O(log n + m) lookup performance to render based upon the scroll position and size of the window.
  • TypeScript Woohoo, superior autocomplete and type safety means fewer bugs in your implementation.
  • Versatility All of the autosizing <Masonry>'s constituent parts are provided via exports so you're not locked into to the implementation. At times it will be useful to have access to those internals. It's also possible to kick the virtualization out of the equation by providing an infinite value to the overscanBy prop, though this would be a terrible idea for large lists.
  • Autosizing The grid will automatically resize itself and its items if the content of the grid items changes or resizes. For example, when an image lazily loads this component will automatically do the work of recalculating the size of that grid item using resize-observer-polyfill.

Quick Start

Check out the demo on CodeSandbox
import {Masonry} from 'masonic'

let i = 0
const items = Array.from(Array(5000), () => ({id: i++}))

const EasyMasonryComponent = props => (
  <Masonry items={items} render={MasonryCard} />
)

const MasonryCard = ({index, data: {id}, width}) => (
  <div>
    <div>Index: {index}</div>
    <pre>ID: {id}</pre>
    <div>Column width: {width}</div>
  </div>
)

API

Components

ComponentDescription
<Masonry>An autosizing masonry grid component that only renders items currently viewable in the window. This component will change its column count to fit its container's width and will decide how many rows to render based upon the height of the window. To facilitate this, it uses <FreeMasonry>, useContainerRect(), and useWindowScroller() under the hood.
<FreeMasonry>A more flexible masonry grid component that lets you define your own width, height, scrollTop, and isScrolling props.
<List>This is just a single-column <Masonry> component.

Hooks

HookDescription
useInfiniteLoader()A utility hook for seamlessly adding infinite scroll behavior to the <Masonry> component. This hook invokes a callback each time the last rendered index surpasses the total number of items in your items array, or the number defined in the totalItems option of this hook.
useContainerRect()A hook used for measuring and tracking the width of the masonry component's container, as well as its distance from the top of your document. These values are necessary to correctly calculate the number/width of columns to render, as well as the number of rows to render.
useWindowScroller()A hook used for measuring the size of the browser window, whether or not the window is currently being scrolled, and the window's scroll position. These values are used when calculating the number of rows to render and determining when we should disable pointer events on the masonry container to maximize scroll performance.

<Masonry>

An autosizing masonry grid component that only renders items currently viewable in the window. This component will change its column count to fit its container's width and will decide how many rows to render based upon the height of the window. To facilitate this, it uses <FreeMasonry>, useContainerRect(), and useWindowScroller() under the hood.

Props
Columns

Props for tuning the column width, count, and gutter of your component.

PropTypeDefaultRequired?Description
columnWidthnumber240YesThis is the minimum column width. Masonic will automatically size your columns to fill its container based on your provided columnWidth and columnGutter values. It will never render anything smaller than this defined width unless its container is smaller than its value.
columnGutternumber0NoThis sets the amount (px) of vertical and horizontal space between grid items.
columnCountnumberundefinedNoBy default, Masonic derives the column count from the columnWidth prop. However, in some situations it is nice to be able to override that behavior (e.g. when creating a <List>.
Item rendering

Props that dictate how individual grid items are rendered.

PropTypeDefaultRequired?Description
renderReact.ComponentClass|React.FCundefinedYesThe component provided here is rendered for each item of your items array (see below). The component here should handle the render props defined below.
itemsany[]undefinedYesAn array of items to render. The data contained at each index is passed to the data prop of your render component. It is also passed to the onRender callback and the itemKey generator. Its length is used for determining the estimated height of the container.
itemHeightEstimatenumber300NoThis value is used for estimating the initial height of the masonry grid. it is vital to the UX of the scrolling behavior and in determining how many items to initially render, so its wise to set this value with some accuracy.
itemAsReact.ReactNode"div"NoYour render component is wrapped with an element that has a style prop which sets the position of the grid item in its container. This is the type of element created for that wrapper. One common use case would be changing this property to li and the Masonry component's as prop to ul.
itemStyleReact.CSSPropertiesundefinedNoYou can add additional styles to the wrapper discussed in itemAs by setting this property.
itemKey(data: any, index: number) => string(_, index) => indexNoThe value returned here must be unique to the item. By default, the key is the item's index. This is ok if your collection of items is never modified. Setting this property ensures that the component in render is reused each time the masonry grid is reflowed. A common pattern would be to return the item's database ID here if there is one, e.g. data => data.id
overscanBynumber2NoThis number is used for determining the number of grid items outside of the visible window to render. The default value is 2 which means "render 2 windows worth of content before and after the items in the visible window". A value of 3 would be 3 windows worth of grid items, so it's a linear relationship. Overscanning is important for preventing tearing when scrolling through items in the grid, but setting too high of a value may create too much work for React to handle, so it's best that you tune this value accordingly.
render props

These are the props provided to the component you set in your render prop.

PropTypeDescription
dataanyThis is the data contained at items[index] of your items prop array.
indexnumberThe index of the item in your items prop array.
widthnumberThe width of the collumn containing this component. This is super useful for doing things like determining the dimensions of images.
Customizing the container element

These props customize how the masonry grid element is rendered.

PropTypeDefaultRequired?Description
asReact.ReactNode"div"NoThis sets the element type of the masonry grid. A common use case would be changing this to ul and itemAs to li.
idstringundefinedNoAdd an ID to the masonry grid container.
classNamestringundefinedNoAdd a class to the masonry grid container.
styleReact.CSSPropertiesundefinedNoAdd inline styles to the masonry grid container.
rolestring"grid"NoChange the aria/a11y role of the container.
tabIndexnumber0NoChange the tabIndex of the container. By default the container is tabbable.
Customizing the window for SSR

These are useful values to set when using SSR because in SSR land we don't have access to the width and height of the window, and thus have no idea how many items to render.

PropTypeDefaultRequired?Description
initialWidthnumber1280NoThe width of the window in SSR.
initialHeightnumber720NoThe height of the window in SSR.
Callbacks
PropTypeDefaultRequired?Description
onRender(startIndex: number, stopIndex: number, items: any[]) => voidundefinedNoThis callback is invoked any time the items rendered in the grid changes.
onRender() arguments
ArgumentTypeDescription
startIndexnumberThe index of the first item currently being rendered in the window
stopIndexnumberThe index of the last item currently being rendered in the window
itemsany[]The array of items you provided to the items prop
Methods

When a ref is provided to this component, you'll have access to the following imperative methods:

MethodTypeDescription
clearPositions() => voidInvoking this method will create a new position cache, clearing all previous stored position values. This is useful if you want the component to reflow when adding new items to the items array, however the best way to trigger a reflow is setting a different unique key prop on the <Masonry> component each time that happens.

<FreeMasonry>

This is a bare bones masonry grid without useWindowScroller() and useContainerRect() hooks doing any magic. It accepts all of the props from <Masonry> except initialWidth and initialHeight.

Additional props
PropTypeDefaultRequired?Description
widthnumberundefinedYesThis sets the width of the grid.
heightnumberundefinedYesThis is the height of the grid's window. If you're rendering <FreeMasonry> inside of a scrollable div for example, this would be the height of that div.
scrollTopnumberundefinedYesThe scroll position of the window <FreeMasonry> is rendering inside. Either the window object scroll position or the scroll position of say, a scrollable div you're rendering inside.
isScrollingbooleanfalseNoWhen this value is true, pointer-events: none; and will-change: contents, height; styles are applied to the grid to maximize scroll performance.
containerRef((element: HTMLElement) => void) | React.MutableRefObject<HTMLElement | null>undefinedNoSets a ref prop on the grid container.

<List>

This is a single-column <Masonry> component. It accepts all of the properties defined in [<Masonry>], except columnGutter, columnWidth, and columnCount.

Additional props
PropTypeDefaultRequired?Description
rowGutternumber0NoThis sets the amount of vertical space in pixels between rendered list items.

useInfiniteLoader()

A React hook for seamlessly adding infinite scrolling behavior to <Masonry> and <List> components.

import {Masonry, useInfiniteLoader} from 'masonic'
import memoize from 'trie-memoize'

const fetchMoreItems = memoize(
  [{}, {}, {}],
  (startIndex, stopIndex, currentItems) =>
    fetch(
      `/api/get-more?after=${startIndex}&limit=${startIndex + stopIndex}`
    ).then(items => {
      // do something to add the new items to your state
    })
)

const InfiniteMasonry = props => {
  const maybeLoadMore = useInfiniteLoader(fetchMoreItems)
  const items = useItemsFromInfiniteLoader()
  return <Masonry {...props} items={items} onRender={maybeLoadMore} />
}
Arguments
ArgumentTypeDescription
loadMoreItems(startIndex: number, stopIndex: number, items: any[]) => anyThis callback will be invoked when more items must be loaded. It may be called multiple times in reaction to a single scroll event. As such, you are expected to memoize/track whether or not you've already received the startIndex, stopIndex, items values to prevent loading data more than once.
optionsInfiniteLoaderOptionsConfiguration object for your loader, see InfiniteLoaderOptions below.
InfiniteLoaderOptions
PropertyTypeDefaultDescription
isItemLoaded(index: number, items: any[]) => boolean(index, items) => items[index] !== undefinedA callback responsible for determining the loaded state of each item. Return true if the item has already been loaded and false if not.
minimumBatchSizenumber16
thresholdnumber16The default value of 16 means that data will start loading when a user scrolls within 16 items of the end of your items prop array.
totalItemsnumber9E9The total number of items you'll need to eventually load (if known). This can be arbitrarily high if not known (e.g., the default value).

useWindowScroller()

A hook used for measuring the size of the browser window, whether or not the window is currently being scrolled, and the window's scroll position. These values are used when calculating the number of rows to render and determining when we should disable pointer events on the masonry container to maximize scroll performance.

import React from 'react'
import {FreeMasonry, useWindowScroller, useContainerRect} from 'masonic'

const MyCustomMasonry = props => {
  const {width, height, scrollY, isScrolling} = useWindowScroller(),
    [rect, containerRef] = useContainerRect(width, height)

  return React.createElement(
    FreeMasonry,
    Object.assign(
      {
        width: rect.width,
        height,
        scrollTop: Math.max(0, scrollY - (rect.top + scrollY)),
        isScrolling,
        containerRef,
      },
      props
    )
  )
}
Arguments
ArgumentTypeDescription
initialWidthnumberThe width of the window when render on the server side. This has no effect client side.
initialHeightnumberThe height of the window when render on the server side. This has no effect client side.
optionsWindowScrollerOptionsA configuration object for the hook. See WindowScrollerOptions below.
WindowScrollerOptions
interface WindowScrollerOptions {
  size?: {
    // Debounces for this amount of time in ms
    // before updating the size of the window
    // in state
    //
    // Defaults to: 120
    wait?: number
  }
  scroll?: {
    // The rate in frames per second to update
    // the state of the scroll position
    //
    // Defaults to: 8
    fps?: number
  }
}
Returns WindowScrollerResult
interface WindowScrollerResult {
  // The width of the browser window
  width: number
  // The height of the browser window
  height: number
  // The scroll position of the window on its y-axis
  scrollY: number
  // Is the window currently being scrolled?
  isScrolling: boolean
}

useContainerRect()

A hook used for measuring and tracking the width of the masonry component's container, as well as its distance from the top of your document. These values are necessary to correctly calculate the number/width of columns to render, as well as the number of rows to render.

import React from 'react'
import {FreeMasonry, useWindowScroller, useContainerRect} from 'masonic'

const MyCustomMasonry = props => {
  const {width, height, scrollY, isScrolling} = useWindowScroller(),
    [rect, containerRef] = useContainerRect(width, height)

  return React.createElement(
    FreeMasonry,
    Object.assign(
      {
        width: rect.width,
        height,
        scrollTop: Math.max(0, scrollY - (rect.top + scrollY)),
        isScrolling,
        containerRef,
      },
      props
    )
  )
}
Arguments
ArgumentTypeDescription
windowWidthnumberThe width of the window. Used for updating the ContainerRect when the window's width changes.
windowHeightnumberThe height of the window. Used for updating the ContainerRect when the window's height changes.
Returns [ContainerRect, (element: HTMLElement) => void]
ContainerRect
PropertyTypeDescription
topnumberThe top value from element.getBoundingClientRect()
widthnumberThe width value from element.getBoundingClientRect()

Differences from react-virtualized/Masonry

There are actually quite a few differences between these components and the originals, despite the overall design being highly inspired by them.

  1. The react-virtualized component requires a <CellMeasurer>, cellPositioner, and cellMeasurerCache and a ton of custom implementation to get off the ground. It's very difficult to work with. In Masonic this functionality is built in using resize-observer-polyfill for tracking cell size changes.

  2. This component will auto-calculate the number of columns to render based upon the defined columnWidth property. The column count will update any time it changes.

  3. The implementation for updating cell positions and sizes is also much more efficient in this component because only specific cells and columns are updated when cell sizes change, whereas in the original a complete reflow is triggered.

  4. The API is a complete rewrite and because of much of what is mentioned above, is much easier to use in my opinion.

LICENSE

MIT

Keywords

FAQs

Package last updated on 23 Apr 2020

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc