masonic

repository·main·Indexed 23 days ago

https://github.com/jaredlunde/masonic

A performant virtualized masonry grid for React, version 4.1.0. It utilizes red-black interval trees to efficiently handle tens of thousands of items. The library provides a high-level <Masonry> component, a single-column <List> component, and an optimized <MasonryScroller>, along with a suite of hooks (such as useMasonry, usePositioner, and useInfiniteLoader) and utilities for building custom masonry implementations.

Tokens
13.5K
Snippets
18
Records
52
Agent score
78%

What's inside masonic

  1. Overview of masonic features

    main

    masonic is a virtualized masonry grid component for React. Key features include:

    • Virtualization: Uses a red-black interval tree for O(log n + m) lookup performance, allowing it to render hundreds of thousands of items efficiently.
    • Autosizing: The grid automatically recalculates item sizes if content changes or resizes (e.g., when an image lazily loads).
    • TypeScript Support: Provides full type safety and autocomplete.
    • Versatility: Provides access to constituent parts of the <Masonry> component. You can disable virtualization by providing an infinite value to the overscanBy prop (not recommended for large lists).
  2. Understand differences between Masonic and react-virtualized/Masonry

    main

    While inspired by react-virtualized/Masonry, Masonic provides a significantly different developer experience:

    • Built-in Size Tracking: Unlike react-virtualized, which requires manual setup of <CellMeasurer>, cellPositioner, and cellMeasurerCache, Masonic has built-in functionality for tracking cell size changes using resize-observer-polyfill.
    • Automatic Column Calculation: Masonic automatically calculates the number of columns to render based on the columnWidth property. The column count updates dynamically whenever columnWidth changes.
    • Efficient Reflows: When cell sizes change, Masonic only updates the specific cells and columns affected, whereas the original triggers a complete reflow.
    • Simplified API: The API has been completely rewritten to be more intuitive and easier to implement.
  3. Understand differences between Masonic and react-virtualized

    main

    Masonic is inspired by react-virtualized but offers several improvements designed to reduce implementation complexity and improve performance:

    • Built-in Size Tracking: Unlike react-virtualized, which requires manual setup of <CellMeasurer>, cellPositioner, and cellMeasurerCache, Masonic has built-in functionality for tracking cell size changes using resize-observer-polyfill.
    • Automatic Column Calculation: Masonic can automatically calculate the number of columns to render based on the columnWidth property. The column count updates dynamically whenever columnWidth changes.
    • Efficient Reflows: When cell sizes change, Masonic only updates the specific cells and columns affected. This is more efficient than the original's approach, which triggers a complete reflow.
    • Simplified API: The API and internals are a complete rewrite intended to be easier to use than the original library.
  4. Use the <Masonry> component for a quick start

    main

    The <Masonry> component is a "batteries included" masonry grid. It is the easiest way to implement a masonry layout because it handles all implementation details automatically: it adjusts column counts based on container width and determines row counts based on the browser window height. It uses several internal hooks like useMasonry, usePositioner, and useScroller to manage layout and performance.

    import * as React from "react";
    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>
    );
  5. Quick Start with the Masonry component

    main

    You can quickly implement a masonry grid using the <Masonry /> component. You need to provide an items array and a render function that defines how each item should be displayed. The render function receives index, data (the item itself), and width as props.

    Note: For large lists, masonic uses a red-black interval tree for high-performance virtualization, ensuring smooth scrolling even with tens of thousands of items.

    import * as React from "react";
    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>
    );
  6. Customize <Masonry> container and SSR

    main

    Customize the grid container element or provide initial dimensions for Server-Side Rendering (SSR):

    Container Customization:

    • as (React.ReactNode, default: "div"): The element type of the masonry grid.
    • id, className, style, role, tabIndex: Standard HTML attributes for the container.

    SSR Support: Since SSR environments lack window dimensions, provide these to prevent layout shifts:

    • initialWidth (number, default: 1280)
    • initialHeight (number, default: 720)
  7. Configure <Masonry> item rendering

    main

    Control how individual items are rendered and managed:

    • render (React.ComponentClass|React.FC): The component used for each item. It receives data, index, and width as props.
    • items (any[]): The array of data to render.
    • itemHeightEstimate (number, default: 300): An estimate of item height used for initial container sizing and scroll calculations. Accuracy is important for UX.
    • itemAs (React.ReactNode, default: "div"): The element type used to wrap your render component. This wrapper holds the positioning styles.
    • itemStyle (React.CSSProperties): Additional styles applied to the itemAs wrapper.
    • itemKey ((data: any, index: number) => string, default: (_, index) => index): A function to return a unique key for each item. Using a unique ID (e.g., data => data.id) ensures component reuse during reflows.
    • overscanBy (number, default: 2): The number of 'window worths' of content to render outside the visible area to prevent tearing during scroll.
  8. Configure <Masonry> columns and gutter

    main

    Use these props to tune the layout of the <Masonry> grid:

    • columnWidth (number, default: 240): The minimum width for a column. The component will automatically fill the container using this value.
    • columnGutter (number, default: 0): The amount of space (px) between grid items (both vertical and horizontal).
    • columnCount (number): An optional override to force a specific number of columns, useful when creating a <List>.
  9. Custom Masonry implementation with useWindowScroller and useContainerRect

    main

    If you need to build a custom masonry implementation (e.g., using <FreeMasonry>), you can combine useWindowScroller and useContainerRect to calculate dimensions and scroll positions.

    useWindowScroller: Returns { width, height, scrollY, isScrolling } for the browser window. useContainerRect: Returns [rect, containerRef] where rect contains the container's top and width relative to the document.

    Example Pattern:

    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
        )
      );
    };
  10. Use the usePositioner hook

    main

    The usePositioner hook is the core of the masonry layout algorithm. It creates the grid cell positioner and cache required by useMasonry(). It determines which cells to render at a specific scroll position and where to place new items.

    Arguments

    • options: An object of type UsePositionerOptions that determines column count and widths.
    • deps: An optional React.DependenciesList. If these dependencies change, the hook creates a new positioner and clears all cached positions.

    Example Integration

    To use usePositioner effectively, it is often paired with useContainerPosition and MasonryScroller to create a custom masonry component.

    import * as React from "react";
    import { usePositioner, useContainerPosition, MasonryScroller } from "masonic";
    
    const MyMasonry = ({ columnWidth = 300, columnGutter = 16, ...props }) => {
      const { width, offset } = useContainerPosition();
      const positioner = usePositioner({ width, columnWidth, columnGutter });
      return <MasonryScroller positioner={positioner} offset={offset} {...props} />;
    };