masonic

A virtualized masonry grid for React built on composable hooks and an O(log n) interval-tree positioner.

Library
npm
v4.1.0
1,409stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
42/100Fair
Development Activity4
Maintenance20
Community44
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
77/100Good
Architecture80
Code Quality82
Innovation75
Learning Curve70

Masonic is a React library for building performant, virtualized masonry grid layouts. Instead of shipping only a drop-in component, it exposes the full set of hooks and utilities used internally — usePositioner, useMasonry, useResizeObserver, useScrollToIndex, useInfiniteLoader — so teams that outgrow the default <Masonry> component can compose their own layout without forking the library. Under the hood it uses a hand-rolled red-black interval tree to determine which cells are visible at a given scroll position, giving O(log n + m) lookup performance instead of the linear scans many masonry libraries rely on, letting it render tens of thousands of grid cells without dropped frames.

The grid autosizes as content loads — for example when an image finishes loading and changes a cell’s height — by wiring a ResizeObserver into the positioner’s cache, and it supports infinite-scroll loading via useInfiniteLoader. It’s TypeScript-first with full type definitions shipped for ESM, CJS, and UMD builds, and virtualization can be disabled entirely by setting overscanBy to Infinity for small lists that still want masonry layout.

What You Get

  • Masonry, MasonryScroller, and List components for common grid and single-column layouts
  • usePositioner, useMasonry, useResizeObserver, useScrollToIndex, and useInfiniteLoader hooks exposing every internal implementation detail individually
  • A red-black interval-tree-based positioner (src/interval-tree.ts) for O(log n + m) visible-cell lookups
  • Full TypeScript type definitions and ESM/CJS/UMD builds published from a single src/index.tsx entry

Common Use Cases

  • Image galleries and Pinterest-style grids with variable-height cards
  • Infinite-scrolling feeds where new items load as the user scrolls
  • Product or listing grids that need to autosize when images or content lazy-load
  • Custom masonry implementations that need direct access to the positioner or interval-tree primitives

Under The Hood

Architecture Masonic is organized as a flat set of composable React hooks and thin wrapper components rather than a monolith: usePositioner (src/use-positioner.ts) owns layout state via a hand-rolled red-black interval tree (src/interval-tree.ts) that maps scroll-position ranges to cell indexes; useMasonry (src/use-masonry.tsx) consumes a Positioner plus a ResizeObserver-backed cache (src/elements-cache.ts, src/use-resize-observer.ts) to decide which cells fall in the visible range and renders only those as React elements; <Masonry> (src/masonry.tsx) is a batteries-included composition root that wires useWindowSize, useContainerPosition, usePositioner, useResizeObserver, and useScrollToIndex together and hands the result to <MasonryScroller>, while <List> reuses the identical stack with columnCount pinned to 1. Because every layer is exported individually from src/index.tsx, an app that outgrows the default component can swap in its own scroll or measurement source without touching the positioner or interval-tree core — the abstraction most other code depends on is the Positioner interface (set/get/update/range), since both useMasonry and <List> rely on its exact shape.

Tech Stack Masonic is a TypeScript library (the large majority of source, targeting ES5 per tsconfig.json) built for React (peer dependency react >=16.8), published via lundle, a build tool that produces ESM, CJS, and UMD bundles from the single src/index.tsx entry per package.json’s exports map. Its runtime dependencies are almost entirely the author’s own micro-utility ecosystem — memoize-one, one-key-map, and several @react-hook packages for events, latest-value tracking, passive layout effects, throttling, and window size/scroll — rather than general-purpose libraries, keeping the dependency tree small and purpose-built. Testing uses Jest with a fast SWC-based transform and React Testing Library; releases are automated with semantic-release triggered from a GitHub Actions workflow, with pnpm as the package manager.

Code Quality Tests are meaningful rather than superficial: dedicated test files cover the interval tree data structure and the resize-observer hook in isolation, while an index-level test exercises the composed Masonry/List behavior in a jsdom environment with a stored snapshot. CI runs type-checking, linting, and coverage-instrumented tests before any release, with coverage reported to Codecov. Error handling is intentionally minimal — a dev-mode-only invariant guards against a common hooks footgun (a changing dependency-array length) — but otherwise the library favors defensive numeric fallbacks over thrown exceptions, consistent with a low-level rendering primitive. Naming is consistent, the codebase is fully typed under strict mode, and ESLint plus Prettier are enforced via pre-commit hooks, though commit activity has slowed considerably in recent history.

API Design The public API is layered for progressive disclosure: a newcomer needs only a single component with an items list and a render prop to get a working grid, while every internal implementation detail is also exported individually for advanced composition, so no forking is required to customize behavior. Hook options are documented with comments directly above each function signature, surfacing inline in editor tooltips without a trip to the README. Full type definitions ship for all three build targets, so consumers get autocomplete regardless of module system; the one rough edge is that the positioner’s dependency array will throw at runtime in development if its length changes between renders, an easy footgun for anyone unfamiliar with the hooks-array-identity convention.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search