When long lists become a performance problem
Scrolling lists are everywhere: social media feeds, dashboards, and enterprise applications. The trouble starts when those lists grow to hundreds, thousands, or even hundreds of thousands of items. A bloated DOM slows initial load, makes rendering sluggish, and multiplies event listeners across every item. Users notice the lag and abandon the page.
How the Angular CDK solves it
Virtual scrolling is the standard fix. It renders only a small window of items that fit on screen, plus a buffer, while presenting a properly sized scrollbar so the user can navigate the full list. As the user scrolls, new subsets are calculated and rendered, reusing DOM nodes where possible.
Angular provides this functionality through the Component Dev Kit (CDK). With a few tweaks to how you iterate over lists, the CDK’s virtual scrolling handles the heavy lifting automatically.
Install the CDK
Start by installing the @angular/cdk package with npm:
npm install --save @angular/cdk
Import the ScrollingModule
Bring ScrollingModule into your application from the @angular/cdk/scrolling package and add it to the module imports:
import {ScrollingModule} from '@angular/cdk/scrolling';
...
imports: [
ScrollingModule
...
]
...
Set up a viewport
Begin with a straightforward component that renders numbers from 0 to 99,999:
@Component({
template: `<div *ngFor="let item of list">{{item}}</div>`
})
export class ScrollComponent {
list = Array.from({length: 100000}).map((_, i) => i);
}
With this approach, the browser renders 100,000 <div> elements. Basic text may survive, but any complexity or event handling in the repeated template will not scale.
Wrap the list in a <cdk-virtual-scroll-viewport> element. This tells the CDK to manage rendering dynamically:
@Component({
template: `<cdk-virtual-scroll-viewport>
<div *ngFor="let item of list">{{item}}</div>
</cdk-virtual-scroll-viewport>`
})
export class ScrollComponent {
list = Array.from({length: 100000}).map((_, i) => i);
}
Since the viewport renders subsets of content on demand, you must give it a fixed height using CSS. You also need to provide an itemSize so the CDK knows how many items to keep in the DOM and how to size the scrollbar accurately:
@Component({
template: `<cdk-virtual-scroll-viewport itemSize="18" style="height:80vh">
<div *ngFor="let item of list">{{item}}</div>
</cdk-virtual-scroll-viewport>`
})
export class ScrollComponent {
list = Array.from({length: 100000}).map((_, i) => i);
}
Finally, replace the *ngFor with *cdkVirtualFor:
@Component({
template: `<cdk-virtual-scroll-viewport itemSize="18" style="height:80vh">
<div *cdkVirtualFor="let item of list">{{item}}</div>
</cdk-virtual-scroll-viewport>`
})
export class ScrollComponent {
list = Array.from({length: 100000}).map((_, i) => i);
}
The viewport now determines which items are visible and iterates only over that subset. When the page loads, you get the items that fit on screen plus a modest buffer. Scrolling triggers rendering of the next appropriate subset:
Next steps with the CDK
This setup covers the basics. For more advanced use cases, the list can be fetched on demand, rather than fully loaded upfront. The CDK’s ScrollingModule and cdkVirtualOf directive support a range of additional configurations, all detailed in the CDK scrolling documentation and a complete working example.



