Row virtualization renders only the visible window of rows, so the Angular Data Grid stays fast with 100,000 rows and beyond — sorting, filtering and selection run across the full dataset.
Virtualization keeps the DOM small no matter how large the dataset is: only the
rows currently in view (plus a small buffer) are rendered as real elements.
Reach for it whenever you bind more rows than the browser can comfortably paint
at once — a few thousand and up. It is on by default
([virtualization]="true") and is mutually exclusive with
pagination.
100,000 rows, virtualized
Only the visible window of rows exists in the DOM — scroll, sort, filter and select across the full dataset. This live demo runs on 100,000 generated rows.
import { Component } from '@angular/core'
import { DataGridComponent } from '@coreui/angular-data-grid'
import type { DataGridColumn, DataGridItem } from '@coreui/angular-data-grid'
const firstNames = ['Alice', 'Bob', 'Carol', 'Dave', 'Eve', 'Frank', 'Grace', 'Heidi', 'Ivan', 'Judy']
const lastNames = ['Smith', 'Jones', 'Brown', 'Taylor', 'Wilson', 'Davies', 'Evans', 'Thomas']
const roles = ['admin', 'editor', 'viewer']
const statuses = ['active', 'pending', 'banned']
@Component({
selector: 'docs-data-grid-virtual-example',
imports: [DataGridComponent],
template: `
<c-data-grid
[columns]="columns"
[items]="items"
[itemKey]="itemKey"
[columnFilters]="true"
[globalFilter]="true"
[rowSelection]="true"
/>
`
})
export class DataGridVirtualExample {
readonly columns: DataGridColumn[] = [
{ key: 'id', label: '#', width: 90 },
{ key: 'name', label: 'Name', width: 200 },
{ key: 'email', label: 'Email', width: 260 },
{ key: 'role', label: 'Role', width: 110 },
{ key: 'status', label: 'Status', width: 110 },
{ key: 'score', label: 'Score', width: 90 }
]
readonly items: DataGridItem[] = Array.from({ length: 100_000 }, (_, i) => {
const name = `${firstNames[i % firstNames.length]} ${lastNames[i % lastNames.length]}`
return {
id: i + 1,
name,
email: `${name.toLowerCase().replace(' ', '.')}${i}@example.com`,
role: roles[i % roles.length],
status: statuses[i % statuses.length],
score: (i * 37) % 1000
}
})
readonly itemKey = (item: DataGridItem) => String(item.id)
} How it works
The grid measures the scroll viewport and renders only the rows that intersect it. Two inputs tune the behavior:
rowHeight— the estimated row height in px (default44) the virtualizer uses to size the scroll area and decide how many rows fit.overscan— extra rows rendered above and below the visible window (default10) to smooth fast scrolling. Raise it if you see blank rows while flinging; lower it to shave DOM nodes.
Fixed row height is assumed in this release — dynamic per-row heights are on the roadmap. For datasets larger than browser memory, hand paging to your backend with server-side data.
See the Performance guide for tuning advice.