When you use Angular in zoneless mode, CDK Virtual Scroll can flicker. The issue is reported on GitHub and a PR exists, but it has not been merged yet.
This article shows how to fix the bug with patch-package.
Bug details
When you enable provideExperimentalZonelessChangeDetection in Angular zoneless mode, virtual-scroll-viewport flickers while scrolling.
The cause is that transform updates become visible. Specifically, when the viewport adjusts content position, applying the transform directly causes flicker.
Fixing it with patch-package
What is patch-package?
patch-package is a tool for patching packages inside node_modules directly. Typical use cases:
- You want to fix a package bug, but the PR is not merged yet
- You want to extend a package, but the PR is not merged yet
- You do not want to bump the package version, but you need a specific bug fix
How it works
patch-package provides a way to apply temporary fixes to packages. First, edit the package in node_modules, then save the diff under a patches directory. After that, running npm install applies the patch automatically through a postinstall script.
This lets you apply a temporary fix without forking the package source. Because patch files live in version control, you can share the same fix across a team.
Implementation steps
1. Install patch-package
First, install patch-package:
npm install --save-dev patch-package
2. Update package.json
Add the following script to the scripts section of package.json:
{
"scripts": {
"postinstall": "patch-package"
}
}
This postinstall script runs automatically after npm install and applies patches from the patches directory.
3. Create the patch
Edit the @angular/cdk source. The change looks like this:
diff --git a/node_modules/@angular/cdk/__ivy_ngcc__/esm2020/scrolling/virtual-scroll-viewport.mjs b/node_modules/@angular/cdk/__ivy_ngcc__/esm2020/scrolling/virtual-scroll-viewport.mjs
index 1234567..89abcdef 100644
--- a/node_modules/@angular/cdk/__ivy_ngcc__/esm2020/scrolling/virtual-scroll-viewport.mjs
+++ b/node_modules/@angular/cdk/__ivy_ngcc__/esm2020/scrolling/virtual-scroll-viewport.mjs
@@ -123,456 +789,123 @@
- this._contentWrapper.nativeElement.style.transform = this._renderedContentTransform;
+ afterNextRender(() => {
+ this._contentWrapper.nativeElement.style.transform = this._renderedContentTransform;
+ this._isChangeDetectionPending = false;
+ const runAfterChangeDetection = this._runAfterChangeDetection;
+ this._runAfterChangeDetection = [];
+ });
This moves the transform update into the afterNextRender phase and prevents flicker.
4. Apply the patch
After editing, run:
npx patch-package @angular/cdk
This creates a file such as patches/@angular+cdk+19.2.14.patch containing the diff.
5. Verify the patch
Check the generated patch file. It should look like this:
diff --git a/node_modules/@angular/cdk/__ivy_ngcc__/esm2020/scrolling/virtual-scroll-viewport.mjs b/node_modules/@angular/cdk/__ivy_ngcc__/esm2020/scrolling/virtual-scroll-viewport.mjs
index 1234567..89abcdef 100644
--- a/node_modules/@angular/cdk/__ivy_ngcc__/esm2020/scrolling/virtual-scroll-viewport.mjs
+++ b/node_modules/@angular/cdk/__ivy_ngcc__/esm2020/scrolling/virtual-scroll-viewport.mjs
@@ -123,456 +789,123 @@
- this._contentWrapper.nativeElement.style.transform = this._renderedContentTransform;
+ afterNextRender(() => {
+ this._contentWrapper.nativeElement.style.transform = this._renderedContentTransform;
+ this._isChangeDetectionPending = false;
+ const runAfterChangeDetection = this._runAfterChangeDetection;
+ this._runAfterChangeDetection = [];
+ });
Managing patches
Commit patch files to your version control system (Git, etc.). Benefits:
- Share the same fix across the team
- Track patch history
- Avoid forgetting to apply patches
When you upgrade the package version, the patch may stop applying and you will need to update it.
Summary
This article showed how to fix CDK Virtual Scroll flicker in Angular zoneless mode with patch-package.
The fix removes Virtual Scroll flicker and makes scrolling smoother.
This is a temporary workaround. When an official Angular fix ships, remove the patch and use the upstream change instead.
See you next time.