https://github.com/ceriousdevtech/ngx-cerious-scroll
Angular implementation for Cerious-Scroll
https://github.com/ceriousdevtech/ngx-cerious-scroll
Last synced: 26 days ago
JSON representation
Angular implementation for Cerious-Scroll
- Host: GitHub
- URL: https://github.com/ceriousdevtech/ngx-cerious-scroll
- Owner: ceriousdevtech
- License: other
- Created: 2026-02-01T06:38:42.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2026-06-12T04:52:43.000Z (about 2 months ago)
- Last Synced: 2026-06-12T06:15:52.980Z (about 2 months ago)
- Language: TypeScript
- Size: 630 KB
- Stars: 4
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
- fucking-awesome-angular - ngx-cerious-scroll - Angular bindings for [Cerious Scroll](https://github.com/ceriousdevtech/cerious-scroll), a high-performance virtual scrolling implementation. (Third Party Components / Scroll)
- awesome-angular - ngx-cerious-scroll - Angular bindings for [Cerious Scroll](https://github.com/ceriousdevtech/cerious-scroll), a high-performance virtual scrolling implementation. (Third Party Components / Scroll)
README
# @ceriousdevtech/ngx-cerious-scroll
[](LICENSE)
[](https://ceriousdevtech.github.io/ngx-cerious-scroll/)
**Angular bindings for [Cerious Scroll™](https://www.npmjs.com/package/@ceriousdevtech/cerious-scroll)** — high-performance virtual scrolling with **O(1) memory**, consistent **60 FPS+**, and **native variable-height support with no height estimation**.
Rows are rendered into the engine's own measured containers via Angular's `EmbeddedViewRef` and committed synchronously, so every row's real height is measured (never estimated) — exactly the guarantee that makes CeriousScroll precise. Because rows stay in your Angular tree, **DI, pipes, and structural directives work normally** inside each row.
---
## Installation
```bash
npm install @ceriousdevtech/ngx-cerious-scroll @ceriousdevtech/cerious-scroll
```
`@angular/core` and `@angular/common` (>= 16) are peer dependencies.
---
## Demo
**[Live demo →](https://ceriousdevtech.github.io/ngx-cerious-scroll/)** — 100,000 rows, fixed/variable-height toggle, imperative jump-to-row, and live viewport stats.
To run locally:
```bash
npm install
npm run build # build the library
npm start # dev server with HMR (http://localhost:4300/)
```
The demo imports the wrapper by its package name, aliased to the built library,
so rebuild the library after editing `projects/ngx-cerious-scroll/src/`.
---
## Quick start (component)
Give the host a height; provide `items` and an ``.
```ts
import { Component } from '@angular/core';
import {
CeriousScrollComponent,
CeriousScrollItemTemplateDirective,
} from '@ceriousdevtech/ngx-cerious-scroll';
@Component({
standalone: true,
imports: [CeriousScrollComponent, CeriousScrollItemTemplateDirective],
template: `
{{ index }} — {{ item.name }}
`,
})
export class List {
items = Array.from({ length: 1_000_000 }, (_, i) => ({ id: i, name: `Item ${i}` }));
}
```
Variable heights need no configuration — just render rows of whatever height; the
engine measures each one.
### Without a full array (huge / sparse data)
```html
```
---
## Directive
`[ceriousScroll]` gives you full control on any host element. Pass a
`TemplateRef` via `[ceriousScrollItemTemplate]`; the directive renders rows
imperatively into the engine's measured containers.
```ts
import { Component, TemplateRef, ViewChild } from '@angular/core';
import { CeriousScrollDirective } from '@ceriousdevtech/ngx-cerious-scroll';
@Component({
standalone: true,
imports: [CeriousScrollDirective],
template: `
{{ index }} — {{ item.name }}
`,
})
export class List {
@ViewChild('row', { static: true }) row!: TemplateRef;
items = /* ... */;
}
```
---
## Component inputs
| Input | Type | Description |
| --- | --- | --- |
| `items` | `readonly TItem[]` | Optional data array. `totalElements` defaults to `items.length`. |
| `totalElements` | `number` | Total item count. Required if `items` is omitted. |
| `getItem` | `(index) => TItem` | Lazy item getter for large/sparse datasets. |
| `itemTemplate` | `TemplateRef<{ $implicit, index }>` | Row template. Alternative to projecting ``. |
| `headerTemplate` | `TemplateRef` | Table mode only. `` of ``s rendered into the engine's `` (see [Table layout](#table-layout)). |
| `options` | `CeriousScrollOptions` | Engine options (keyboard/touch/wheel/scrollbar/`layout`/etc.). Read once at creation. |
| `autoRender` | `boolean` | Re-render on scroll/resize/data changes. Default `true`. |
The row is provided by the projected `` or the `itemTemplate` input. Apply `class` / `style` directly to `` — it's a block-level host (set a height!).
### Outputs
| Output | Payload | Description |
| --- | --- | --- |
| `viewportChange` | `CeriousViewportChangeDetail` | Normalized viewport-change (wheel/touch/keyboard/scrollbar). |
| `measuredViewport` | `MeasuredViewportRange` | Measured range after each render pass. |
| `scrollerReady` | `CeriousScroll` | The underlying engine instance, once ready. |
### Imperative API (via template reference)
```html
…
```
```ts
@ViewChild(CeriousScrollDirective) scroll!: CeriousScrollDirective;
// scroll.jumpToElement(500);
// scroll.scrollToPercentage(50);
// scroll.reset();
// scroll.render();
// scroll.recalculate(); // drop cached heights + re-measure (see Notes)
// scroll.hostRef?.scroller; // the raw engine
```
---
## Table layout
Pass `[ceriousScrollOptions]="{ layout: 'table' }"` to render real `` / `` / `` rows with a frozen header and native column alignment. The row template returns the row's `` cells; `[ceriousScrollHeaderTemplate]` provides the `` row (it updates via change detection):
```html
@for (c of columns; track c.key) { {{ c.label }} }
{{ row(index).id }}
{{ row(index).name }}
{{ row(index).email }}
```
`` exposes the same via `[headerTemplate]` and ``.
- The **header template** renders into the engine's `` (same `` as the rows → native column alignment, frozen header).
- The **row template's root nodes must be ``s** (don't wrap them in a structural directive at the root — that hides the cells from the directive's recycle re-append).
- **`table.autoSizeColumns`** measures column widths once and pins them (auto-sized + stable); or use `table.columnWidths`. Variable row heights work as usual.
- CSS: `border-collapse: separate` and an **opaque `` background** (see the core README's [Table Layout](https://github.com/ceriousdevtech/cerious-scroll#-table-layout-layout-table) notes).
---
## Notes
- **No height estimation.** Rows are committed synchronously via
`EmbeddedViewRef.detectChanges()` so the engine measures real `offsetHeight`.
Later size changes are picked up by the engine's built-in `ResizeObserver`.
- **`options` are read at creation.** Changing `options` after init has no
effect; recreate the host (e.g. with `*ngIf` toggling) to apply new engine
options.
- **Changing the item count** recreates the engine internally (scroll position
is preserved). Mutating items without changing the count just re-renders the
content in place (cheap; Angular patches each row, so focus/selection survive)
— it does **not** discard cached heights, so editable grids that produce a new
`items` array on every edit don't trigger a full viewport re-measure.
- **If every rendered row's height changes at once** (e.g. a density/layout
switch) the cached heights become stale and rows can misalign until the next
scroll. Call `recalculate()` on the directive instance right after the change
to drop the height cache and re-measure. Don't call it on routine edits — a
single cell edit keeps its row's size, and the engine's built-in
`ResizeObserver` picks up any incidental resize on its own.
---
## License
Licensed by **Cerious DevTech LLC** under the **MIT License** (see `LICENSE`).
📧 info@ceriousdevtech.com