UNPKG

listine

Version:

Angular virtual scroll component supporting variable item heights.

131 lines (92 loc) 5.08 kB
# Listine — Angular Variable-Height Virtual Scroller [![npm version](https://img.shields.io/npm/v/listine.svg)](https://www.npmjs.com/package/listine) [![npm downloads](https://img.shields.io/npm/dm/listine.svg)](https://www.npmjs.com/package/listine) [![license](https://img.shields.io/npm/l/listine.svg)](https://github.com/hrssh11/listine/blob/main/LICENSE) [![Angular](https://img.shields.io/badge/Angular-21-dd0031.svg)](https://angular.dev) A lightweight, high-performance virtual scroll component for Angular that renders **only the items currently visible** — and unlike most virtual scrollers, it works with **variable (unknown) item heights** out of the box. Render lists with **millions of rows** smoothly, without freezing the browser. --- ## Why Listine? Most virtual scrollers (Angular CDK, PrimeNG, etc.) require a **fixed item size**. Listine measures each row as it renders, so it handles dynamic, content-driven heights without you predefining anything. - **True dynamic height support** — measures real DOM heights, no fixed `itemSize` required. - **Built for huge lists** — only visible rows live in the DOM, even with 1,000,000+ items. - **Signals-based & zoneless-friendly** — modern Angular APIs (`input()`, `output()`, `signal()`, `effect()`), `OnPush` change detection. - **Standalone component** — no NgModule, drop it straight into any Angular app. - **Flexible templating** — render each item with your own template, with access to the item and its index. - **Lazy-load ready**`scrollToEnd` output makes infinite scroll trivial. --- ## Installation ```bash npm install listine ``` > Requires **Angular 21+** (`@angular/core` and `@angular/common` `^21.0.0`). --- ## Quick Start Import the standalone component and use it in your template. ```ts import { Component } from "@angular/core"; import { VariableVirtualScrollComponent } from "listine"; @Component({ selector: "app-root", standalone: true, imports: [VariableVirtualScrollComponent], template: ` <listine-variable-virtual-scroll [items]="options" [buffer]="10" [viewportHeight]="600" [itemTemplate]="itemTemplate" (scrollToEnd)="loadMore()" > <ng-template #itemTemplate let-option let-i="index"> <p>{{ option.label }} (ID: {{ option.value }})</p> </ng-template> </listine-variable-virtual-scroll> `, }) export class AppComponent { options = Array.from({ length: 1_000_000 }, (_, i) => ({ label: `Item ${i}`, value: i.toString(), })); loadMore() { // fetch / append the next page of data } } ``` --- ## API ### Inputs | Input | Type | Default | Description | | ------------------- | ------------------- | ------- | --------------------------------------------------------------------------- | | `items` | `any[]` | `[]` | The full list of data to virtualize. | | `itemTemplate` | `TemplateRef<any>` | — | **Required.** Template used to render each item. | | `viewportHeight` | `number` | `400` | Height of the scrollable viewport, in pixels. | | `buffer` | `number` | `5` | Extra rows rendered above/below the viewport for smoother scrolling. | | `initialItemHeight` | `number` | `50` | Estimated row height (px) used before a row is measured. | | `panelOpen` | `boolean` | `false` | Recalculates layout when shown inside a panel/overlay (prevents blank view).| | `scrollToTopTrigger`| `number` | `0` | Change this value to reset the list to the top (e.g. after filter/search). | ### Outputs | Output | Payload | Description | | --------------- | -------- | ------------------------------------------------------- | | `scrollEmitter` | `number` | Emits the current scroll position on every scroll. | | `scrollToEnd` | `void` | Emits when the user reaches the end — ideal for lazy load. | ### Template context The item template receives: - `$implicit` — the current item (e.g. `let-option`) - `index` — the absolute index in `items` (e.g. `let-i="index"`) ### Public methods | Method | Description | | --------------- | ----------------------------------------------------- | | `scrollToTop()` | Scrolls the list back to the top and recalculates rows. | --- ## Tips - Put the component in a container with a defined height; the scroll area is driven by `viewportHeight`. - For filter/search, bump `scrollToTopTrigger` so the user starts from the top of the new results. --- ## Changelog See [CHANGELOG.md](https://github.com/hrssh11/listine/blob/main/CHANGELOG.md) for the full release history. ## Author - [@hrssh11](https://www.github.com/hrssh11) ## License [MIT](LICENSE)