Ignite UI for Angular 22.2.0 shipped on September 30, 2026, and the 22.2.1 patch followed on October 5, 2026. The release adds IgxVirtualScrollComponent, a standalone Angular virtual scroll for long lists with variable item sizes, Angular Signal Forms support through [formField] in ten form controls, and standard CSS scrollbar styling in place of the ::-webkit-scrollbar-* pseudo-elements. Breaking changes land in scrollbar-theme, grid-summary-theme, the button directives and the button group, and ng update migrates the theming arguments and the removed multiSelection input automatically.
The thread through 22.2, and the reason I think its breaking changes are worth taking, is that the library stops maintaining its own versions of things the platform already does. Form controls read their state from Signal Forms, scrollbars use the standard CSS properties, and summary cells take their border width from the grid they sit in. Each move gives up some control, and every section below says where.
Ignite UI for Angular 22.2 at a glance
Ignite UI for Angular is a UI component library by Infragistics for Angular applications, including a high-performance data grid and charting. The table lists every Ignite UI for Angular 22.2 change that needs a decision during an upgrade, with the version that introduced it.
| Change | Version | Type | Action on upgrade |
|---|---|---|---|
IgxVirtualScrollComponent |
22.2.0 | New component | None, opt in |
Signal Forms ([formField]) in ten form controls |
22.2.0 | New feature | None |
igc-color-picker with ngModel and formControlName |
22.2.0 | New feature | None |
Combo, Simple Combo and Excel-style filtering list on IgxVirtualScrollComponent |
22.2.0 | Internal change | Update tests and CSS that target the old list markup |
| Standard CSS scrollbars | 22.2.0 | Breaking (theming) | ng update, then a visual check |
grid-summary-theme border widths removed |
22.2.0 | Breaking (theming) | ng update, then review renamed custom properties |
IgxButtonGroupComponent: multiSelection removed, values typed |
22.2.0 | Breaking | ng update for templates, manual for TypeScript |
IgxButtonDirective and IgxIconButtonDirective members removed |
22.2.0 | Breaking | Manual |
| PDF export shading and merged Pivot Grid row dimension cells | 22.2.0 | Behavioral | Review exported reports |
| Pivot Grid and Tree Grid template directives in standalone arrays | 22.2.1 | New feature | None |
GridBaseAPIService and IgxFilteringService deprecations |
22.2.1 | Deprecation | Plan replacements |
Tree Grid beginAddRowByIndex and filter() behavior |
22.2.1 | Behavioral | Review calls |
IgxVirtualScrollComponent: a new Angular virtual scroll for long lists
IgxVirtualScrollComponent is a new Ignite UI for Angular 22.2 component that renders only the items inside the viewport, plus an over-scan buffer, so the DOM holds a window of rows instead of the whole list. Import it, together with the igxVirtualItem template directive, from igniteui-angular/virtual-scroll.
IgxVirtualScrollComponent covers the cases that usually force a custom solution:
- Vertical and horizontal orientation, with right-to-left support.
- Variable item sizes, measured in the DOM. Items that have not been measured yet take the average measured size, so the scrollbar tracks the real content.
- Recycled item elements: a scroll step updates only the items entering the window, and
keyFunctionlets an element follow its item whendatais reordered. scrollToIndexwithscrollIntoView-style alignment (start,center,end,nearest), corrected until it lands on the item.- Infinite scrolling through the
dataRequestoutput, and paged data throughdataWindow, with a scrollbar that spans the whole collection. - Lists larger than the browser’s maximum scroll size.
Example: an infinite activity log in Angular
The scenario I would reach for IgxVirtualScrollComponent first is a log or activity feed: rows have different heights, and data arrives a page at a time. The Angular component below loads a first page, appends pages from dataRequest, keys rows by id, and maps each row’s position onto aria-posinset and aria-setsize so assistive technology still knows where it is in the list.
import { Component, signal } from '@angular/core';
import {
IgxVirtualScrollComponent,
IgxVirtualItemDirective,
} from 'igniteui-angular/virtual-scroll';
import type { VirtualScrollDataRequest } from 'igniteui-angular/virtual-scroll';
interface LogEntry {
id: number;
message: string;
}
// Stand-in for a paged backend call. Replace with your own data access.
async function fetchEntries(startIndex: number, count: number): Promise<LogEntry[]> {
return Array.from({ length: count }, (_, i) => ({
id: startIndex + i,
message: `Log entry ${startIndex + i}`,
}));
}
@Component({
selector: 'app-log-viewer',
imports: [IgxVirtualScrollComponent, IgxVirtualItemDirective],
template: `
<igx-virtual-scroll
[data]="entries()"
[keyFunction]="byId"
(dataRequest)="loadMore($event)"
style="height: 480px;">
<ng-template igxVirtualItem let-entry let-i="index" let-count="count">
<div role="listitem" [attr.aria-posinset]="i + 1" [attr.aria-setsize]="count">
{{ entry.message }}
</div>
</ng-template>
</igx-virtual-scroll>
`,
})
export class LogViewerComponent {
// A signal keeps the view in sync in zoneless and zone-based apps alike.
entries = signal<LogEntry[]>([]);
byId = (entry: LogEntry) => entry.id;
constructor() {
// dataRequest does not fire for an empty array, so load the first page yourself.
fetchEntries(0, 50).then(page => this.entries.set(page));
}
async loadMore(request: VirtualScrollDataRequest) {
const page = await fetchEntries(request.startIndex, request.count);
// data is compared by reference: append into a new array, never push in place.
this.entries.update(current => [...current, ...page]);
}
}
The snippet assumes an Angular 22 application with igniteui-angular 22.2 installed and a theme applied; the Ignite UI for Angular setup reference covers both. Two lines carry most of the weight. entries is a signal, so the view updates in zoneless and zone-based applications alike. loadMore assigns a new array, because IgxVirtualScrollComponent compares data by reference and does not react to an in-place push.
Limitations of IgxVirtualScrollComponent
IgxVirtualScrollComponent virtualizes one list of templated items. For tabular data with columns, sorting and filtering, the Angular Data Grid is still the component to use. Check these constraints, all documented in the IgxVirtualScrollComponent README, before adopting it:
- The host needs a fixed height (vertical) or a fixed width (horizontal). The default styles already set
overflow. - Items are measured by their border box, so margins accumulate as drift. Use
paddingon the item, orgapon a wrapper. - Recycled elements keep any DOM state that is not bound. A checkbox toggled without
[checked]shows up on another item, so bind all item state. dataRequestis not emitted for an empty initial array, and it is not emitted at all whiledataWindowis bound.- With
dataWindow, the engine keeps one size entry per index oftotalCount: roughly 17 MB per million items, per the 22.2 IgxVirtualScrollComponent README. - Inside a drop-down, dialog or other container that is hidden until it opens, the first render has no viewport to measure, so rows appear on the next frame. Read the rendered rows after
layoutCompleteresolves.
Combo, Simple Combo and Excel-style filtering now run on IgxVirtualScrollComponent
In Ignite UI for Angular 22.2, IgxComboComponent, IgxSimpleComboComponent and the Excel-style filtering search list virtualize their items with IgxVirtualScrollComponent instead of the igxFor directive. Each row is measured in the DOM once it renders, and the measured size replaces the estimate it started from.
The upgrade risk here sits in test code and CSS overrides, not in application logic. The list markup changed: igx-display-container and the igx-vhelper--vertical scrollbar are replaced by the igx-virtual-scroll host and its igx-virtual-item row wrappers, so end-to-end selectors and style rules that reach into the old elements stop matching. The public virtualizationState and totalItemCount members are unchanged. Only the internal virtualScrollContainer member, marked @hidden @internal, changed type, and the Excel-style filtering search list no longer has a virtDir.
The switch also brings fixes to the Angular Combo and Simple Combo:
- Remote pages no longer change position before their replacements arrive, and an already loaded initial range is not requested again.
- A change to a positive
totalItemCountrefreshes the list without rebindingdata. - Each combo resolves its selection once per change detection check, which reduces selection work.
- The internal virtual scroll containers no longer add a nested
listrole, which keeps the existing listbox and option semantics intact.
IgxDropDownComponent now accepts a content-projected igx-virtual-scroll as well as *igxFor, which keeps working as documented. Selection and navigation behave the same with either.
Angular Signal Forms support in Ignite UI for Angular form controls
Ignite UI for Angular 22.2 binds ten form controls to Angular Signal Forms through [formField]: igxInput, igx-checkbox, igx-switch, igx-radio-group, igx-select, igx-combo, igx-simple-combo, igx-date-picker, igx-time-picker and igx-date-range-picker. Validity, touched, dirty, disabled and required state are read from the signal-backed field, and no ReactiveFormsModule import is needed.
Signal Forms ship as public API in Angular 22 under @angular/forms/signals. Reactive and template-driven forms keep working with the same controls: 22.2 adds a path, it does not replace one.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { FormField, disabled, form, required, submit } from '@angular/forms/signals';
import { IGX_INPUT_GROUP_DIRECTIVES } from 'igniteui-angular/input-group';
import { IgxSelectComponent, IgxSelectItemComponent } from 'igniteui-angular/select';
import { IgxCheckboxComponent } from 'igniteui-angular/checkbox';
@Component({
selector: 'app-signup',
imports: [FormField, IGX_INPUT_GROUP_DIRECTIVES, IgxSelectComponent, IgxSelectItemComponent, IgxCheckboxComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<form (submit)="onSubmit($event)">
<igx-input-group>
<label igxLabel>Name</label>
<input igxInput [formField]="signup.name" />
@for (error of signup.name().errors(); track error.kind) {
<igx-hint>{{ error.message }}</igx-hint>
}
</igx-input-group>
<igx-select [formField]="signup.role">
<label igxLabel>Role</label>
@for (r of roles; track r) {
<igx-select-item [value]="r">{{ r }}</igx-select-item>
}
</igx-select>
<igx-checkbox [formField]="signup.terms">Accept terms</igx-checkbox>
<button type="submit">Sign up</button>
</form>
`
})
export class SignupComponent {
model = signal({ name: '', role: '', terms: false });
roles = ['Admin', 'User'];
signup = form(this.model, (path) => {
required(path.name, { message: 'Name is required' });
required(path.role);
required(path.terms);
disabled(path.role, { when: ({ valueOf }) => valueOf(path.name) === '' });
});
onSubmit(event: Event) {
event.preventDefault();
submit(this.signup, async () => undefined);
}
}
The example comes from the Ignite UI for Angular components agent skill shipped in the 22.2.0 package. igx-select opens an overlay, so the application also needs provideAnimations() in app.config.ts. submit() marks every field as touched, so invalid controls show their error state the way markAllAsTouched() does in reactive forms.
Related Angular form changes in 22.2.0:
igx-checkbox,igx-switchandigx-radio-groupreportrequiredandaria-requiredforValidators.requiredTrue, asigxInputalready did.igx-radio-groupimplementssetDisabledState, socontrol.disable(),enable()and the Signal Formsdisabledrule reach the radio buttons.- Inputs, selects, combos and pickers no longer paint the invalid style while an async validator is pending.
igx-radio-groupfireschangeonce per selection, marks the bound control as touched on blur, and re-evaluates the invalid state when the control’s status changes.IgcFormControlDirectivesupportsigc-color-picker, so the Web Components color picker binds withngModelandformControlNameinside an Angular application.
Why your Ignite UI for Angular scrollbar theme looks different after 22.2
Ignite UI for Angular 22.2 styles scrollbars with the standard scrollbar-color and scrollbar-width CSS properties instead of the ::-webkit-scrollbar-* pseudo-elements. As a result, scrollbar-theme() keeps two working arguments, $sb-thumb-bg-color and $sb-track-bg-color, and every other argument is accepted but ignored.
Picture a design system with a half-rem scrollbar, a rounded thumb and a darker hover color, applied across an Angular application that hosts the Angular Data Grid. After the upgrade the theme still compiles. The thumb is square, the hover color is gone, and the width is whatever the browser draws for auto. Nothing errors, and that silence is why I would put this change first on a visual-regression checklist.
The cause is the standard property set itself: it exposes two colors and three width keywords (auto, thin, none), so a length cannot drive scrollbar-width.
scrollbar-theme() argument |
Status in 22.2 |
|---|---|
$sb-thumb-bg-color, $sb-track-bg-color |
Work as before |
$sb-size |
No effect; set --sb-width: thin, or scrollbar-width: thin on the scrolling element |
$sb-thumb-bg-color-hover, $sb-track-bg-color-hover |
No effect |
$sb-thumb-min-height, $sb-thumb-border-color, $sb-thumb-border-size, $sb-thumb-border-radius |
No effect |
$sb-track-border-color, $sb-track-border-size |
No effect |
$sb-corner-bg, $sb-corner-border-color, $sb-corner-border-size |
No effect |
The ng update migration for 22.2.0 removes the ignored arguments, including $sb-size, from existing scrollbar-theme(...) calls. A theme that set the design above changes like this:
@use 'igniteui-angular/theming' as *;
// Before ng update: these arguments still compile in 22.2, but most are now ignored
$my-scrollbar: scrollbar-theme(
$sb-size: 0.5rem,
$sb-thumb-bg-color: var(--ig-primary-300),
$sb-thumb-bg-color-hover: var(--ig-primary-500),
$sb-thumb-border-radius: 0.25rem,
$sb-track-bg-color: var(--ig-gray-100),
);
:root {
@include tokens($my-scrollbar);
}
@use 'igniteui-angular/theming' as *;
// After ng update: only the two colors the standard properties can express remain
$my-scrollbar: scrollbar-theme(
$sb-thumb-bg-color: var(--ig-primary-300),
$sb-track-bg-color: var(--ig-gray-100),
);
:root {
@include tokens($my-scrollbar);
// scrollbar-width takes auto, thin or none, so a length can no longer set the thickness
--sb-width: thin;
}
Three more details decide whether the result matches the design:
- If you override the scrollbar tokens on a nested scope, re-declare
scrollbar-colorandscrollbar-widthon that scope as well. Both properties resolve theirvar()references on the element that declares them, and descendants inherit the resolved value. The library does this itself forigx-gridand the rest of the grid family,igx-query-builder,igx-column-actionsand the Excel-style filtering menus, whose scrollbars follow their own background and foreground colors. - Scrollbar arrow buttons cannot be styled through the standard properties, and
scrollbar-width: thinremoves them where the platform draws them. - Firefox versions before 64 do not support
scrollbar-colororscrollbar-widthand render default scrollbars.
Is there a way back to a rounded thumb or a hover color? Not through scrollbar-theme() in 22.2. If a design system requires either, that requirement needs a conversation with its owner before the upgrade, not after it.
Grid summary borders now follow the Angular Data Grid border widths
In Ignite UI for Angular 22.2, grid-summary-theme() no longer accepts $border-width and $pinned-border-width, and the --ig-grid-summary-border-width and --ig-grid-summary-pinned-border-width CSS custom properties are removed. A summary cell takes its border width from grid-theme(): $header-border-width for the cell separator and $pinned-border-width for the pinned border.
The reasoning is visual continuity. A summary border continues a column border, so it can no longer be thicker or thinner than the border it continues. Border styles and colors stay themable through $border-style, $pinned-border-style, $border-color and $pinned-border-color, and through their CSS custom properties.
The ng update migration drops the removed arguments from grid-summary-theme(...) calls and renames the removed custom properties to the grid properties that now drive them. Review every replacement where a summary-only width was set: those grid properties also style the Data Grid’s own borders, so a migrated override now changes more than the summary row.
Button group and button directive API changes in Ignite UI for Angular 22.2
Ignite UI for Angular 22.2 types the IgxButtonGroupComponent values input as IButtonGroupButton[], removes the multiSelection input that was deprecated in 16.1.0, and removes five members from IgxButtonDirective and IgxIconButtonDirective.
The IButtonGroupButton interface describes each button configured through values: label, icon, ripple, selected, disabled and togglable. Every item requires a label, so collections without one stop compiling, and values now defaults to an empty array instead of undefined. Buttons rendered from values are tracked by label rather than by object identity: replacing an item with a new object that has the same label updates the existing button, so keep labels unique.
multiSelection becomes selectionMode="multi". The ng update migration rewrites it in templateUrl files and inline template strings. Inline templates that contain ${} interpolations, and references in TypeScript code, need a manual edit. The Angular template below imports IGX_BUTTON_GROUP_DIRECTIVES from igniteui-angular/button-group:
<!-- Before: multiSelection is removed in 22.2 -->
<igx-buttongroup [multiSelection]="true">
<button igxButton [selected]="true">Bold</button>
<button igxButton>Italic</button>
<button igxButton>Underline</button>
</igx-buttongroup>
<!-- After -->
<igx-buttongroup selectionMode="multi">
<button igxButton [selected]="true">Bold</button>
<button igxButton>Italic</button>
<button igxButton>Underline</button>
</igx-buttongroup>
On IgxButtonDirective and IgxIconButtonDirective, the element, role, focused, select() and deselect() members are gone. Use nativeElement instead of element, set role in the template, and bind selected instead of calling select() or deselect(). IgxButtonGroupComponent keeps its selectButton() and deselectButton() methods.
Pivot Grid and Tree Grid templates in standalone Angular components (22.2.1)
Ignite UI for Angular 22.2.1 adds two template directives to the standalone directive arrays, so both templates can be declared inside the grid in standalone components:
IgxPivotRowDimensionHeaderTemplateDirective(igxPivotRowDimensionHeader) is now part ofIGX_PIVOT_GRID_DIRECTIVESandIgxPivotGridModule, for the Pivot Grid row dimension header template.IgxRowLoadingIndicatorTemplateDirective(igxRowLoadingIndicator) is now exported fromigniteui-angular/grids/tree-gridand included inIGX_TREE_GRID_DIRECTIVES, for the Tree Grid row loading indicator shown withloadChildrenOnDemand.
Deprecations and behavioral changes in the Ignite UI for Angular grids (22.2.1)
Ignite UI for Angular 22.2.1 deprecates four GridBaseAPIService members and one IgxFilteringService method, all scheduled for removal in a future version. Each has a public replacement on the grid or the column:
| Deprecated API | Replacement |
|---|---|
GridBaseAPIService.get_rec_id_by_index |
The record at the index in the grid data, and its primaryKey value |
GridBaseAPIService.sort_decoupled |
The grid groupBy method |
GridBaseAPIService.get_filtered_data |
The grid filteredData property |
GridBaseAPIService.getSortStrategyPerColumn (protected) |
The column sortStrategy property |
IgxFilteringService.filter_global |
The grid filteringExpressionsTree property |
prepare_sorting_expression and prepare_grouping_expression, and prepare_sorting_expression in GridServiceType, now take a single expressions collection instead of an array of collections.
Two behavioral changes can alter existing code paths:
- In the Tree Grid,
beginAddRowByIndex(index, true)now opens the add row UI for a child of the row atindex, as documented; previously the parent was the row atindex - 1.beginAddRowByIndex(0)now adds the first record, as in the other grids, instead of logging a warning. - In the Data Grid, Tree Grid, Hierarchical Grid and Pivot Grid, calling
filterwithout a condition for a column that has no filtering expressions now throwsInvalid condition or Expression Tree!, even when afilteringevent handler sets expressions for that column.
PDF export changes in Ignite UI for Angular 22.2
In Ignite UI for Angular 22.2, IgxPdfExporterService shades summary rows like the header row of the exported table, and the shading follows the showTableBorders option. The reasoning in the changelog: a summary closes the rows above it the way the header opens them, so it should not read as one more record.
Pivot Grid exports change in two more ways. Row dimension cells are shaded the same way, because they head the record they sit on. A row dimension value that repeats down consecutive records is drawn once, in a single merged cell, the way the Pivot Grid merges its own row headers. Values merge only under the same parent dimension, and a merged cell that would cross the bottom of a page is cut there and reopened under the headers of the next page.
Exported reports look different after the upgrade, so any process that parses or compares PDF output needs a fresh baseline.
Other fixes in Ignite UI for Angular 22.2
Ignite UI for Angular 22.2.0 and 22.2.1 also ship these fixes, grouped by component:
| Component | Fix | Version |
|---|---|---|
| Data Grid, Tree Grid, Hierarchical Grid, Pivot Grid | Copying with only columns selected copies the selected columns’ data | 22.2.1 |
| Data Grid, Tree Grid, Hierarchical Grid, Pivot Grid | pin(index) no longer throws when the target pinning area is empty, and unpin(index) no longer throws when all columns are pinned |
22.2.1 |
| Data Grid, Tree Grid, Hierarchical Grid, Pivot Grid | navigateTo with a callback no longer throws with remote virtualization when the target row is not loaded |
22.2.1 |
| Tree Grid | Excel-style filtering lists column values with a custom strategy that extends FilteringStrategy |
22.2.1 |
| Hierarchical Grid | expandChildren applies to child grids rendered later, and nested row islands added with @if register under their parent |
22.2.1 |
| Grid Lite | A sort or filter from the UI runs the data pipeline once, so dataPipelineConfiguration hooks no longer fire twice |
22.2.0 |
| Checkbox, Ripple | The tick mark renders correctly inside CSS-scoped subtrees that use a different design system than the application | 22.2.0 |
| Navigation Drawer | Fast touch movements below the pan threshold no longer toggle the drawer | 22.2.0 |
| Month Picker | aria-activedescendant follows the active month or year during keyboard navigation |
22.2.0 |
| Migrations | Theme-argument migrations preserve comments and process stylesheets outside sourceRoot |
22.2.0 |
The full set of changes, including fixes not listed here, is in the Ignite UI for Angular CHANGELOG on GitHub.
How to upgrade to Ignite UI for Angular 22.2
Upgrading to Ignite UI for Angular 22.2 starts with Angular 22, because the 22.2 packages declare Angular 22 as their peer dependency. Then:
- Run
ng updateto list the available migrations, thenng update igniteui-angular, using@infragistics/igniteui-angularinstead if the project depends on the licensed package. - Search TypeScript files, and inline templates with
${}interpolations, formultiSelectionand replace it withselectionMode. - Replace
element,role,focused,select()anddeselect()on IgxButtonDirective and IgxIconButtonDirective. - Give every item in a button group
valuescollection a uniquelabel. - Update end-to-end selectors and CSS that target
igx-display-containerorigx-vhelper--verticalinside combos and Excel-style filtering. - Review the custom properties the migration renamed in
grid-summary-themeoverrides. - Compare scrollbars and summary borders visually, and set
--sb-width: thinwhere a thinner scrollbar is required. - Replace calls to the
GridBaseAPIServiceandIgxFilteringServicemembers deprecated in 22.2.1.
Frequently asked questions about Ignite UI for Angular 22.2
Does Ignite UI for Angular 22.2 have breaking changes?
Yes. Ignite UI for Angular 22.2.0 turns most scrollbar-theme arguments into no-ops, removes the grid-summary-theme border width arguments, removes the button group multiSelection input, types the button group values input, and removes five members from IgxButtonDirective and IgxIconButtonDirective. The ng update migration handles the theming arguments and multiSelection in templates, and the rest needs manual edits.
Which Angular version does Ignite UI for Angular 22.2 require?
Ignite UI for Angular 22.2 requires Angular 22. The igniteui-angular 22.2 packages declare version 22 of @angular/core, @angular/common, @angular/forms and @angular/animations as peer dependencies.
Does IgxVirtualScrollComponent replace the igxFor directive?
No. The igxFor directive keeps working as documented, and IgxDropDownComponent accepts either igxFor or a content-projected igx-virtual-scroll. In Ignite UI for Angular 22.2, the Combo, the Simple Combo and the Excel-style filtering search list switched internally to IgxVirtualScrollComponent.
Does ng update migrate Ignite UI for Angular 22.2 automatically?
Partly. The 22.2.0 migrations remove the ignored scrollbar-theme arguments, drop the removed grid-summary-theme border widths and rename their custom properties, and replace multiSelection with selectionMode in component templates. TypeScript references to multiSelection, inline templates with interpolations, the removed button directive members, and test selectors for the old combo markup need manual changes.
What did Ignite UI for Angular 22.2.1 deprecate?
Ignite UI for Angular 22.2.1 deprecates get_rec_id_by_index, sort_decoupled, get_filtered_data and the protected getSortStrategyPerColumn on GridBaseAPIService, and filter_global on IgxFilteringService. Their replacements are the grid data with its primaryKey value, the grid groupBy method, the grid filteredData property, the column sortStrategy property and the grid filteringExpressionsTree property.


