This content originally appeared on DEV Community and was authored by Dharmen Shah
Angular Material 20 brings significant improvements to the library with new features, enhanced accessibility, performance optimizations, and breaking changes that modernize the codebase. This release focuses on better user experience, improved theming capabilities, and streamlined APIs.
New Features
Tonal Button Support
Angular Material 20 introduces tonal buttons, a new Material Design 3 variant that provides a middle ground between filled and outlined buttons.
import { Component } from '@angular/core';
@Component({
selector: 'app-button-example',
template: `
<button matButton="tonal">Basic</button>
<button matButton="tonal" disabled>Disabled</button>
<a matButton="tonal" href="#">Link</a>
`
})
export class ButtonExampleComponent {}
The button appearance can now be set dynamically:
@Component({
selector: 'app-dynamic-button',
template: `
<button [matButton]="buttonAppearance()">Basic</button>
<button [matButton]="buttonAppearance()" disabled>Disabled</button>
<a [matButton]="buttonAppearance()" href="#">Link</a>
<br />
<mat-radio-group aria-label="Select an option" [(ngModel)]="buttonAppearance">
<mat-radio-button value="elevated">Elevated</mat-radio-button>
<mat-radio-button value="outlined">Outlined</mat-radio-button>
<mat-radio-button value="tonal">Tonal</mat-radio-button>
<mat-radio-button value="filled">Filled</mat-radio-button>
</mat-radio-group>
`,
imports: [MatButtonModule, FormsModule, MatRadioModule],
})
export class DynamicButtonComponent {
buttonAppearance = signal<'elevated' | 'outlined' | 'tonal' | 'filled'>(
'elevated'
);
}
Demo:
Filled Card Variant
Cards now support a filled variant that provides better visual hierarchy and surface distinction. You can use the appearance="filled"
property to set the variant.
<mat-card class="example-card" appearance="filled">
<mat-card-header>
<div mat-card-avatar class="example-header-image"></div>
<mat-card-title>Shiba Inu</mat-card-title>
<mat-card-subtitle>Dog Breed</mat-card-subtitle>
</mat-card-header>
<img
mat-card-image
src="https://material.angular.dev/assets/img/examples/shiba2.jpg"
alt="Photo of a Shiba Inu"
/>
<mat-card-content>
<p>
The Shiba Inu is the smallest of the six original and distinct spitz
breeds of dog from Japan. A small, agile dog that copes very well with
mountainous terrain, the Shiba Inu was originally bred for hunting.
</p>
</mat-card-content>
<mat-card-actions>
<button matButton>LIKE</button>
<button matButton>SHARE</button>
</mat-card-actions>
</mat-card>
Demo:
Enhanced Dialog Control
The dialog component now includes a closePredicate
option that provides fine-grained control over when dialogs can be closed.
export interface DialogData {
animal: string;
name: string;
}
@Component({
selector: 'dialog-overview-example',
templateUrl: 'dialog-overview-example.html',
imports: [MatFormFieldModule, MatInputModule, FormsModule, MatButtonModule],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DialogOverviewExample {
readonly animal = signal('');
readonly name = model('');
readonly dialog = inject(MatDialog);
openDialog(): void {
const canClose = <Result = string>(result: Result | undefined): boolean => {
return typeof result === 'string' && result.toLowerCase() === 'dog';
};
const dialogRef = this.dialog.open<
DialogOverviewExampleDialog,
DialogData,
string | undefined
>(DialogOverviewExampleDialog, {
data: { name: this.name(), animal: this.animal() },
closePredicate: canClose,
});
dialogRef.afterClosed().subscribe((result) => {
if (result !== undefined) {
this.animal.set(result);
}
});
}
}
@Component({
selector: 'dialog-overview-example-dialog',
templateUrl: 'dialog-overview-example-dialog.html',
imports: [
MatFormFieldModule,
MatInputModule,
FormsModule,
MatButtonModule,
MatDialogTitle,
MatDialogContent,
MatDialogActions,
MatDialogClose,
],
})
export class DialogOverviewExampleDialog {
readonly dialogRef = inject(MatDialogRef<DialogOverviewExampleDialog>);
readonly data = inject<DialogData>(MAT_DIALOG_DATA);
readonly animal = model(this.data.animal);
}
Demo:
Drag and Drop Enhancements
The CDK drag-drop module introduces the resetToBoundary
method for better boundary management. This is helpful if the boundary-element’s size is changed dynamically and you want to make sure that the dragged element stays within the boundary.
@Component({
selector: 'cdk-drag-drop-boundary-example',
templateUrl: 'cdk-drag-drop-boundary-example.html',
styleUrl: 'cdk-drag-drop-boundary-example.css',
imports: [CdkDrag, MatButtonModule],
})
export class CdkDragDropBoundaryExample {
@ViewChild('boundaryElement') boundaryElement: ElementRef<HTMLElement>;
@ViewChild('dragElement') dragElement: ElementRef<HTMLElement>;
@ViewChild(CdkDrag) dragInstance: CdkDrag;
setBoundary(height: string, width: string) {
this.boundaryElement.nativeElement.style.height = height;
this.boundaryElement.nativeElement.style.width = width;
}
resetToBoundary() {
this.dragInstance.resetToBoundary();
}
}
Demo:
Autocomplete Backdrop Support
Autocomplete now supports overlay backdrops for better focus management. You can provide a hasBackdrop
property using MAT_AUTOCOMPLETE_DEFAULT_OPTIONS
injection token.
@Component({
// rest
providers: [
{
provide: MAT_AUTOCOMPLETE_DEFAULT_OPTIONS,
useValue: { hasBackdrop: true },
},
],
})
export class AutocompleteAutoActiveFirstOptionExample implements OnInit {
// content reduced for brevity
}
Demo:
Breaking change
Angular Material 20 includes breaking changes that require migration. The library provides schematics to help with token renames and other updates.
Token Renames
# Update token names automatically
ng update @angular/material
# Or run specific schematics
ng generate @angular/material:token-rename
Above schematic will do the following:
- Rename any CSS variables beginning with
--mdc-
to be--mat-
- Rename Angular Material component token CSS variables that were renamed so that the base component’s name came first. For example,
--mat-circular-progress
will be renamed to--mat-progress-spinner
. One more, from--mat-tonal-button-
to--mat-button-tonal
(component goes first, then variant). To view the full list, check out the schematic code on GitHub.
Other Changes
- Angular Material now automatically respects the user’s motion preferences set at the system level through the
prefers-reduced-motion
media query. - Form fields now better preserve externally set
aria-describedby
attributes across all form controls. - Form fields now use optimized DOM access patterns and
ResizeObserver
for better performance. - The overlay system now provides tree-shakeable alternatives for better bundle optimization.
- Chip inputs now properly handle placeholders and the
disabledInteractive
property. - The slider component now properly handles null values and improved token management.
- Tabs now handle zero animation duration properly, preventing flickering issues.
Conclusion
Angular Material 20 represents a significant step forward in the library’s evolution, bringing Material Design 3 features, improved accessibility, better performance, and modernized APIs. The new tonal buttons, filled cards, enhanced dialogs, and performance optimizations make this a compelling upgrade for Angular applications.
The breaking changes, while requiring some migration effort, help streamline the API surface and remove deprecated patterns. The provided schematics and migration tools help automate much of the upgrade process.
For detailed migration instructions and breaking change information, consult the official Angular Material changelog.
Angular Material Blocks
I am running a limited time 20% discount on Personal & Teams licenses for lifetime access on Angular Material Blocks! Do not forget to check it out and grab this deal!
This content originally appeared on DEV Community and was authored by Dharmen Shah