npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bianca-ioana-colta/mat-multi-sort

v1.0.2

Published

Multi-column sorting for Angular Material tables (mat-table). Drop-in replacement for matSort and mat-sort-header. Supports Angular and Angular Material 16 to 22.

Downloads

535

Readme

@bianca-ioana-colta/mat-multi-sort

Multi-column sorting for Angular Material tables (mat-table).

Click several column headers and the table is sorted by all of them, in the order you clicked them. Each sorted column shows its arrow and its priority number (1, 2, 3...).

  • Works with Angular / Angular Material 16, 17, 18, 19, 20, 21 and 22.
  • Drop-in replacement for matSort / mat-sort-header.
  • Works with standalone components and with NgModules.
  • Client-side sorting (MatTableDataSource) or server-side sorting (you receive the full sort list).

Contents

  1. Requirements
  2. Install the package
  3. Update the component (.ts)
  4. Update the template (.html)
  5. Run and check
  6. Optional features
  7. Troubleshooting
  8. API reference

1. Requirements

Your project must already have Angular Material installed, in the same major version as @angular/core.

Check:

npm ls @angular/core @angular/material @angular/cdk

If @angular/material is missing, install it first:

ng add @angular/material

Your table should already work with a normal mat-table. The steps below assume you start from a table that uses (or would use) the standard Angular Material sort: matSort + mat-sort-header.


2. Install the package

Run this in the folder that contains your app's package.json:

npm install @bianca-ioana-colta/mat-multi-sort

Check that package.json now contains:

"dependencies": {
  "@bianca-ioana-colta/mat-multi-sort": "^1.0.2"
}

If your company .npmrc points to a private registry and the install fails with E401 / E404, add --registry https://registry.npmjs.org/ to the command.


3. Update the component (.ts)

Open the component that contains the table, for example users-table.component.ts.

3.1 Imports at the top of the file

Remove (if you have it):

import { MatSort, MatSortModule } from '@angular/material/sort';

Add:

import {
  MatMultiSort,
  MatMultiSortHeader,
  MatMultiSortState,
  connectMultiSort,
} from '@bianca-ioana-colta/mat-multi-sort';

3.2 The imports array of the component

Using NgModules instead of standalone components? Skip this and go to 3.2 (NgModule).

Remove MatSortModule and add MatMultiSort and MatMultiSortHeader:

@Component({
  selector: 'app-users-table',
  standalone: true,
  imports: [
    MatTableModule,
    // MatSortModule,      <- remove
    MatMultiSort,       // <- add
    MatMultiSortHeader, // <- add
  ],
  templateUrl: './users-table.component.html',
})

3.2 (NgModule apps)

In the module that declares your table component (for example shared.module.ts):

import { MatMultiSortModule } from '@bianca-ioana-colta/mat-multi-sort';

@NgModule({
  declarations: [UsersTableComponent],
  imports: [
    MatTableModule,
    // MatSortModule,     <- remove (unless other tables still use matSort)
    MatMultiSortModule, // <- add
  ],
})
export class SharedModule {}

3.3 The @ViewChild

Replace:

@ViewChild(MatSort) sort!: MatSort;

with:

@ViewChild(MatMultiSort, { static: true }) sort!: MatMultiSort;

3.4 Connect the sort to the data source

In ngAfterViewInit (or wherever you set dataSource.sort):

Replace:

this.dataSource.sort = this.sort;

with:

connectMultiSort(this.dataSource, this.sort);

Keep everything else as it is: paginator, filter, and your custom sortingDataAccessor (if you have one) keep working. connectMultiSort uses your sortingDataAccessor for every sorted column.

Sorting on the server instead? Do not call connectMultiSort. See 6.3 Server-side sorting.

Full example (.ts)

import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import {
  MatMultiSort,
  MatMultiSortHeader,
  MatMultiSortState,
  connectMultiSort,
} from '@bianca-ioana-colta/mat-multi-sort';

export interface User {
  name: string;
  age: number;
  city: string;
}

@Component({
  selector: 'app-users-table',
  standalone: true,
  imports: [MatTableModule, MatMultiSort, MatMultiSortHeader],
  templateUrl: './users-table.component.html',
})
export class UsersTableComponent implements AfterViewInit {
  @ViewChild(MatMultiSort, { static: true }) sort!: MatMultiSort;

  readonly displayedColumns = ['name', 'age', 'city'];
  readonly dataSource = new MatTableDataSource<User>([
    { name: 'Ana', age: 30, city: 'Cluj' },
    { name: 'Bogdan', age: 25, city: 'Iasi' },
    { name: 'Carmen', age: 30, city: 'Brasov' },
  ]);

  ngAfterViewInit(): void {
    connectMultiSort(this.dataSource, this.sort);
  }
}

4. Update the template (.html)

Open the template of the same component, for example users-table.component.html.

4.1 The <table> element

Replace matSort with matMultiSort:

<!-- before -->
<table mat-table [dataSource]="dataSource" matSort>
  <!-- after -->
  <table mat-table [dataSource]="dataSource" matMultiSort></table>
</table>

If you had matSortActive / matSortDirection for an initial sort, remove them and use 6.1 Initial sort instead.

4.2 Every sortable header

Replace mat-sort-header with mat-multi-sort-header on every <th>:

<!-- before -->
<th mat-header-cell *matHeaderCellDef mat-sort-header>Name</th>

<!-- after -->
<th mat-header-cell *matHeaderCellDef mat-multi-sort-header>Name</th>

The same goes for the bound form:

<!-- before -->
<th mat-header-cell *matHeaderCellDef [mat-sort-header]="'name'">Name</th>

<!-- after -->
<th mat-header-cell *matHeaderCellDef [mat-multi-sort-header]="'name'">Name</th>

Important: search the file for mat-sort-header and make sure none is left. If one remains after removing MatSortModule, Angular fails with NG8002: Can't bind to 'mat-sort-header' since it isn't a known property of 'th'.

The sort id is the matColumnDef name by default, so mat-multi-sort-header without a value is enough when the column name matches the property of the row (row.name). Give an explicit id only when it differs: mat-multi-sort-header="fullName".

start="desc", disabled and disableClear work the same as on mat-sort-header.

Full example (.html)

<table mat-table [dataSource]="dataSource" matMultiSort>
  <ng-container matColumnDef="name">
    <th mat-header-cell *matHeaderCellDef mat-multi-sort-header>Name</th>
    <td mat-cell *matCellDef="let row">{{ row.name }}</td>
  </ng-container>

  <ng-container matColumnDef="age">
    <th mat-header-cell *matHeaderCellDef mat-multi-sort-header start="desc">Age</th>
    <td mat-cell *matCellDef="let row">{{ row.age }}</td>
  </ng-container>

  <ng-container matColumnDef="city">
    <th mat-header-cell *matHeaderCellDef mat-multi-sort-header>City</th>
    <td mat-cell *matCellDef="let row">{{ row.city }}</td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>

5. Run and check

npm start

Open the page with the table and check:

| Action | Expected result | | ------------------------------------------- | ------------------------------------------------------------------------- | | Hover a header | A faint arrow appears. | | Click a header once | Sorted by that column (start direction, asc by default). Arrow visible. | | Click a second header | Sorted by both. Numbers 1 and 2 appear next to the arrows. | | Click a sorted header again | Its direction is reversed. The priority stays the same. | | Click it a third time | The column is removed from the sort; the others move up. | | Move the mouse away from an unsorted header | No arrow is shown. |


6. Optional features

6.1 Initial sort

Component:

protected readonly INITIAL_SORT: MatMultiSortState[] = [
  { active: 'age', direction: 'desc' },
  { active: 'name', direction: 'asc' },
];

Template:

<table mat-table [dataSource]="dataSource" matMultiSort [matMultiSortState]="INITIAL_SORT"></table>

6.2 "Clear sorting" button

<button type="button" (click)="sort.clear()">Clear sorting</button>

6.3 Server-side sorting

Do not call connectMultiSort. Listen to matMultiSortChange and send the list to your API:

<table mat-table [dataSource]="rows" matMultiSort (matMultiSortChange)="onSort($event)"></table>
onSort(sorts: MatMultiSortState[]): void {
  // e.g. [{ active: 'age', direction: 'desc' }, { active: 'name', direction: 'asc' }]
  // first item = highest priority; empty array = no sorting
  this.loadData(sorts);
}

6.4 Read the current sort

const current = this.sort.sorts(); // readonly MatMultiSortState[]

6.5 Arrow position and colours

<th mat-header-cell *matHeaderCellDef mat-multi-sort-header arrowPosition="before">Name</th>
.mat-multi-sort-header {
  --mat-multi-sort-arrow-color: #1976d2;
  --mat-multi-sort-order-color: #1976d2;
}

7. Troubleshooting

| Problem | Fix | | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | NG8002: Can't bind to 'mat-sort-header' | A mat-sort-header is still in the template. Replace it with mat-multi-sort-header. | | NG8002: Can't bind to 'mat-multi-sort-header' / matMultiSortState | MatMultiSort and MatMultiSortHeader (or MatMultiSortModule) are missing from imports. | | Clicking headers does nothing | <table> still has matSort instead of matMultiSort, or connectMultiSort is not called. | | Only one column is sorted at a time | You still assign this.dataSource.sort = this.sort instead of connectMultiSort(this.dataSource, this.sort). | | Dates sort as text | Set dataSource.sortingDataAccessor and return row.date.getTime() for the date column. | | npm install fails with E401 / E404 | Your .npmrc uses a private registry. Add --registry https://registry.npmjs.org/. | | ERESOLVE peer dependency error | @angular/material / @angular/cdk major version is different from @angular/core, or is older than 16. |


8. API reference

MatMultiSort - directive [matMultiSort]

Extends MatSort, so MatTableDataSource, matSortChange, start, disableClear and matSortDisabled keep working (they describe the first / primary sort).

| Member | Description | | --------------------------------------------- | ------------------------------------------------------- | | @Input() matMultiSortState | Sets the sort list (no change event is emitted). | | @Output() matMultiSortChange | Emits MatMultiSortState[] after a click or clear(). | | sorts: Signal<readonly MatMultiSortState[]> | Current sorts, in priority order. | | clear() | Removes all sorts and emits matMultiSortChange. |

MatMultiSortHeader - component [mat-multi-sort-header]

| Input | Description | | ----------------------- | --------------------------------------------------------- | | mat-multi-sort-header | Sort id. Defaults to the matColumnDef name. | | start | 'asc' or 'desc': first direction for this column. | | disableClear | The third click goes back to start instead of removing. | | disabled | Column cannot be sorted. | | arrowPosition | 'before' or 'after' (default). |

MatMultiSortState

interface MatMultiSortState {
  active: string; // column id
  direction: 'asc' | 'desc';
}

Helpers

  • connectMultiSort(dataSource, sort): client-side multi-column sorting for MatTableDataSource.
  • sortByMultiSort(data, sorts, accessor): returns a sorted copy of an array (for custom data sources).

MatMultiSortModule

NgModule that exports MatMultiSort and MatMultiSortHeader, for apps that use NgModules.


License

MIT (c) 2026 Bianca Colta. See LICENSE.