What’s new in Angular Material 15

Duncan Faulkner
Published in
9 min readJan 16, 2023

In November 2022, the Angular team released version 15 of Angular, and alongside that, they also released Angular Material version 15. This post looks at what’s new and what’s changed in Angular Material v15.

For some time now the Angular Material team have been refactoring Angular Material components to be based on the Material Design Components for the web library.

What are Material Design Components?

Material design is a system that helps the collaboration between designers and developers to build products quickly, using best practices, tools and guidelines for user interface design. There are three libraries in total, one for Web, one for Flutter and Android all of which are based on the Material Design Specification.

Angular Material is based on Material Design, but it was not fully compliant with the specification. But now with the release of Angular Material 15, the library is more aligned, with improved accessibility with improved future adoption.

For more information on the Material Design Specification check out the official documentation here.

What’s new?

Out of all the components in the library 22 have had some sort of change, most of them have been styling changes, with a few API changes to some and two that have been completely rewritten.

For backwards compatibility, the old implementation is now deprecated but is still available (for the moment).

The old mat-table can be imported by using the legacy version, for example:

import { MatLegacyTableModule } from '@angular/material/legacy-table';


The upgrade and migration process is very good and updates most of your code to the new version.

First, run the update command this will migrate your code base to use the legacy version, the imports will be as shown above.

ng update @angular/material^15

Once the update is complete we can run the migrate tool to switch to the MDC version of the components.

ng g @angular/material:mdc-migration

This will update as much as possible including Typescript, styles and templates. It’s also possible to do a partial upgrade as both the new and legacy can run side by side (as long as it’s not in the same ngModule), the script will prompt for the directories to change or whether to do the whole project.

? Limit the migration to a specific directory? (Enter the relative path such as 'src/app/shared' or leave blank for all directories) 
? What components do you want to migrate? (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)
◉ Tooltip
◉ Button
◉ Card
❯◉ Checkbox
◉ Chips
◉ Dialog
◉ Form Field, Input, Select, Autocomplete

Running the mdc-migration

Once the migration is complete we can check for // TODO (mdc-migration)… in our code base.

After running the migration and fixing the TODOs, you will need to manually check that your application is working, including unit tests as some of the DOM structure has changed, if you need to update unit tests that use Angular Material components it might be worth changing over to use the component harness.

Library-wide changes

Things to be aware of after migration:

  • The size, colour, spacing, shadows, and animations have all changed slightly.
  • Changes to the DOM structure for improved accessibility across all components.
  • CSS classes applied to components use the mat-mdc- prefix instead of mat-.

So make sure things look correct in your application.

Let’s take a look at the changes

The full list and description of changes can be found here, in this post I will cover a few of the more interesting ones.

These are all components with a change (style, API or Behaviour):

  • Autocomplete — Style changes only
  • Button — Style and API changes*
  • Card — Style changes only
  • Checkbox — Style changes, changes to event behaviour*
  • Chips — Complete rewrite*
  • Core — Style changes only
  • Dialog — Style changes, changes to change detection behaviour
  • Form-Field — Style changes, some appearances removed, API changes*
  • Input — Style changes only
  • List — Style changes, API changes*
  • Menu — Style changes, API changes
  • Paginator — Style changes only
  • Progress-bar — Style changes only
  • Progress-spinner — Style changes only
  • Radio — Style changes only
  • Select — Style changes only
  • Slide-toggle — Style changes only*
  • Slider — Complete rewrite*
  • Snack-bar — Style changes, API changes*
  • Table — Style changes only
  • Tabs — Style changes, API changes
  • Tooltip — Style changes only


One change to the library that’s not a component is themes and a new themeable density has now been added to all components. This new property is based on a density scale which starts with the default of zero and each whole number down (-1,-2 etc…) reduces the size by 4px, this affects the space around items so a negative number reduces the space between items and a positive number increases the space between items.

@import '@angular/material' as mat;

$theme: mat.define-light-theme((
color: ...

// adds density level 0 styles
@include mat.all-component-themes($theme);
@import '@angular/material' as mat;

// or if you prefer a different density level
$theme: mat.define-light-theme((
color: ...,
density: -1

@include mat.all-component-themes($theme);


The main changes to the button component are:

  • The height and width of an Icon button are now 48px instead of 40px.
  • Hover, Focus and Active have slightly different state colours to improve contrast ratios.
  • Letter-Spacing is now 1.25px instead of normal.
  • The FAB button now supports text with the extended input attribute.

The mixin for the buttons has now been split into three separate mixins.

  • Normal button: mat.mdc-button-theme (default, raised, stroked and flat).
  • Icon button: mat.mdc-icon-button-theme.
  • FAB: mat.mdc-fab-theme.
  • Icons are placed before the button text, use iconPositionEnd to add the icon after the button text.
  • Icons within the button content inherit the text font size.


The main changes to the checkbox are mainly styling related but the checkbox click now triggers the native checkbox element instead of the original shim. As the native checkbox click has strange behaviour when calling preventDefault you should not call preventDefault.

The touch targets are now 40px up from 16px making them more accessible. Check your application layouts as these may now overlap other components. It’s possible to match the previous size by changing the density to -1 for the checkbox.

@use '@angular/material' as mat;
@include mat.checkbox-density(-1);

Due to a change in the heuristics, the checkbox colour may be changed to white or black based on the theme of the application, whereas previously this would have set the theme's background colour. It's now based on what has the most contrast against the primary colour.

The focus state has been made slightly darker improving the contrast ratio.

You will need to specifically target the checkbox label to override the typography properties as text styles are not inherited.

The ripple will remain visible after toggling the checkbox using a mouse instead of animating out.


The chip component has been rewritten and split into three variants, the original was interaction-pattern and did not work for all use cases. The original mat-chip-list was role=“listbox”, the new variants are:

  • mat-chip-listbox with mat-chip-option — this is the closest to the original interaction pattern.
  • mat-chip-grid with mat-chip-row — for text and chip interaction.
  • mat-chip-set with mat-chip — custom accessibility pattern.

During migration, the legacy mat-chip-list will be converted to mat-chip-listbox to minimise changes between them. You will need to decide whether these should be changed to mat-chip.grid or mat-chip.set after migration depending on your application.


There have been a few changes to the form-field.

Legacy and Standard appearances have been deprecated for a while now but have now been removed. This also means that to get a floating-label a mat-label is required previously Legacy promoted placeholders to a floating-label.

There is one line of space underneath the field for hint or error messages. There is now a fixed or dynamic property that can either reserve the space (fixed) or add the space when required to display a hint or error message.

Mat-hint text is now darker and larger to comply with W3C text guidelines.

The matPrefix and matSuffix have been replaced with matTextPrefix and matTextSuffix and matIconPrefix and matIconSuffix. The directives matTextPrefix/matTextSuffix are baseline-aligned and matIconPrefix/matIconSuffix are centre aligned.

The never property has also been removed, this was only available on legacy appearance and was used to stop the placeholder from being promoted to a floating-label.


This component has undergone quite a few changes, the API has support for text wrapping, with improved integration with the Material Design Specification.

The mat-line directive is commonly used within a span element, each span representing a line, with the first line being the primary one. The API has now been split into two directives, these are matListItemTitle and matListItemLine.

Text that is not a part of a matListItem, is referred to as unscoped content and requires an additional line.

<span matListItemTitle>Title</span>
Second line

The list can now automatically infer the number of lines (in the example above, the list will render two lines). The API now provides the ability to specify how many lines on the mat-list-item to manually set the wrapping.

<mat-list-item lines="3">
<span matListItemTitle>Title</span>
This text will wrap into the third line. Space for three lines is acquired by the
list item.

With these new directives the text will never wrap, it is only unscoped content that will wrap/take up the remaining space based on the number of lines set.

In addition to the two new directives two have been renamed, these are:

  • matListIcon is now matListItemIcon
  • matListAvatar is now matListItemAvatar

There is a new directive called matListItemMeta this is to put content into the meta section of the list item (this is typically the end of the list). Previously unscoped content in a list item was added to the meta section.

Migration steps

  • change first matLine to matListItemTitle
  • change remaining matLine(s) to matListItemLine
  • change all matListIcon to matListItemIcon
  • change all matListAvatar to matListItemAvatar
  • Wrap all unscoped content (text not in a matLine) in a matListItemMeta

Slide Toggle

This component has changed from an <input type = “checkbox”> to a <button role= “switch”> and no longer responds to native form validation, so you may need to consider an alternative validation.

The touch target has been made much larger, so you should check your application for space so touch targets are not over lapping. It’s possible to reduce the size by specifying a density -1 in the theme.

@use '@angular/material' as mat;
@include mat.slide-toggle-density(-1);


This component has also been completely rewritten and has changed from a single mat-slider element to a mat-slider with one or two inputs (two are for the new Range slider).

  <!-- Single slider -->
<input matSliderThumb>

<!-- New Range slider -->
<input matSliderStartThumb>
<input matSliderEndThumb>

The new Range slider uses two input elements the matSliderStartThumb and matSliderEndThumb these are positioned at the start and end of the slider, to show the value of the slider we need to add the discrete directive.

These two directives provide the following properties:

  • @Input() value: number
  • @Output() valueChange: EventEmitter<number>
  • @Output() dragEnd: EventEmitter<MatSliderDragEvent>
  • @Output() dragStart: EventEmitter<MatSliderDragEvent>
  • percentage: number
  • blur
  • focus

A new discrete property has been added to the mat-slider this replaces the thumbLabel and shows the value indicator.

<!-- Before -->
<mat-slider thumbLabel></mat-slider>

<!-- After -->
<mat-slider discrete>
<input matSliderThumb>

The tickInterval has been replaced with showTickMarks to add tick marks to a slider, the interval will match your slider’s step.

<!-- Before -->
<mat-slider tickInterval="5" step="5"></mat-slider>

<!-- After -->
<mat-slider step="5" showTickMarks>
<input matSliderThumb>

The displayValue has been removed, to provide a value indicator use the displayWith.

<!-- Before -->
<mat-slider [displayValue]="formatLabel"></mat-slider>

<!-- After -->
<mat-slider [displayWith]="formatLabel">
<input matSliderThumb>
 formatLabel = (value: number) => {
return value + 'k';

The valueText property has been removed, we have the option to use the displayWith or the native aria-valuetext.

<!-- Before -->
<mat-slider [valueText]="myValueText"></mat-slider>

<!-- After (Option 1) -->
<input [attr.aria-valuetext]="myValueText" matSliderThumb>

<!-- After (Option 2) -->
<mat-slider [displayWith]="formatLabel">
<input matSliderThumb>

The orientation (horizontal/vertical) and the invert properties have been removed as they are no longer part of the Material Design Specification.


For the most part, there aren’t many changes to the text-based snack-bar, with the simple snack-bar with an Action button, use the MDC button and include the theming mixin.

For custom-structured content (openFromComponent, openFromTemplate) there are a few new directives.

  • matSnackbarLabel — to mark text that’s displayed
  • matSnackBarActions — to mark the element containing the action buttons
  • matSnackBarAction — to mark individual buttons

Not specifying these will treat the custom component/template as text.

In unit tests that open a snack-bar, will need to call flush before accessing the content.

I’m excited about these changes to the Angular Material library and can’t wait to explore them further.

Join us at ng-conf 2023!

ng-conf | June 14–15, 2023
Workshops | June 12–13, 2023
Location | Salt Lake City, UT

ng-conf 2023 is a two-day single-track conference focused on building the Angular community. Come be a part of this amazing event, and meet the Angular Team and the biggest names in the community. Learn the latest in Angular, build your network, and grow your skills.