Frontend Development 36 min read

Implementing a Virtual Waterfall List in Vue 3 + TypeScript with Dynamic Height Support

This article provides a step‑by‑step tutorial on building a virtual waterfall (masonry) list component in Vue 3 using TypeScript, covering component structure, props, state management, rendering logic, handling variable item heights via temporary DOM measurement, performance optimizations, and practical usage examples.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Implementing a Virtual Waterfall List in Vue 3 + TypeScript with Dynamic Height Support

Overview

The article explains how to create a virtual waterfall (masonry) list component in Vue 3 with TypeScript, aiming to reproduce the scrolling performance of platforms like Xiaohongshu while supporting both fixed‑height and dynamic‑height items.

Component Structure and Styles

The DOM consists of three layers: <div class="fs-virtual-waterfall-container"> (scroll container), <div class="fs-virtual-waterfall-list"> (relative list), and <div class="fs-virtual-waterfall-item"> (absolutely positioned items). Styles are defined using SCSS to set container size, overflow, and item positioning.

Props and Initial State

Props include gap , column , pageSize , optional enterSize , and a request function for data fetching. Reactive state tracks loading status, pagination, the raw data list, scroll viewport dimensions, and two queue structures: one for column layout ( queueState ) and one for the flattened render list ( renderList ).

Core Algorithms

1. Column Height Calculation : A computed property computedHeight determines the minimum and maximum column heights, which are used to place new items in the shortest column.

2. Item Generation : generatorItem creates an IRenderItem containing the original data, calculated y offset, height, and inline style (width, height, transform).

3. Render List Filtering : renderList filters the flattened cardList to keep only items whose y + h overlaps the current scroll window ( start ‑ end ).

Dynamic Height Handling

For items whose height cannot be known beforehand (e.g., text‑only cards), a temporary DOM container ( #temporary-list ) is used. Items are first rendered invisibly, their real heights are measured with getBoundingClientRect() , and the results are stored in itemSizeInfo . After measurement, the temporary container is hidden and the normal virtual list is updated.

Data Loading and Scroll Handling

The component initializes by fetching the first page, calculating item widths, and mounting the temporary list for height measurement. On scroll, handleScroll updates start and, if the scroll reaches the minimum column height, either loads more data (when the queue is exhausted) or adds a configurable number of items ( enterSize ) from the already‑fetched data.

Responsive Re‑calculation

A resize observer triggers reComputedQueue , which recomputes column widths, clears the queue, and re‑mounts a batch of items (default pageSize ) to avoid full re‑measurement of thousands of elements.

Performance Optimizations

The article proposes a “batch‑enter” strategy: instead of re‑measuring the entire data set on every resize or scroll, only a small batch (e.g., enterSize or column * 2 ) is processed, dramatically reducing DOM operations and keeping scrolling smooth even with large data volumes.

Usage Example

A parent component demonstrates how to import FsVirtualWaterfall , provide a mock getData promise, and supply a slot template for rendering each card, including optional animation styles.

Conclusion

The final component achieves a performant virtual waterfall list with support for variable item heights, lazy loading, responsive layout, and extensible animation, and the source code is linked to a GitHub repository.

Frontendperformancetypescriptvuevirtual-listwaterfall layout
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.