Skip to content
Angular Material — UI Components Complete Guide

Angular Material — UI Components Complete Guide

DodaTech Updated Jun 6, 2026 8 min read

Angular Material is Google’s official UI component library for Angular. This complete guide covers installation, core components, theming, and practical patterns for building professional user interfaces.

What You’ll Learn

  • Install and configure Angular Material in your project
  • Use Material buttons, cards, dialogs, and form controls
  • Build data tables with sorting, pagination, and filtering
  • Create navigation with toolbars, sidenavs, and tabs
  • Customize themes with Material’s theming system

Why Angular Material Matters

A polished UI builds user trust. Doda Browser’s settings panel and Durga Antivirus Pro’s dashboard use Angular Material components to deliver consistent, accessible, and responsive interfaces. Material follows Google’s Material Design guidelines — a design language used by millions of apps worldwide — so users feel immediately familiar with your interface.

    flowchart LR
    A["Angular Basics & CLI"] --> B["**Angular Material**"]
    B --> C["Building Complete Apps"]
    style B fill:#f97316,stroke:#c2410c,color:#fff
  
Prerequisites: Angular basics — components, forms, and routing. Install the CLI first with https://tutorials.dodatech.com/frameworks/angular/cli/.

Installation

ng add @angular/material

Running this command does three things:

  1. Installs the @angular/material and @angular/cdk packages
  2. Asks you to choose a pre-built theme (indigo-pink, deeppurple-amber, rose-red, custom)
  3. Asks whether to include typography (recommended: yes) and animations (recommended: yes)
Always use ng add instead of npm install for Angular Material. The ng add command automatically configures themes, typography, and module imports.

Core Components

Buttons and Indicators

Angular Material provides several button styles, each for a different visual hierarchy:

<!-- Text button (low emphasis) -->
<button mat-button>Basic</button>

<!-- Raised button (medium emphasis, adds shadow) -->
<button mat-raised-button color="primary">Raised</button>

<!-- Flat button (medium emphasis, no shadow) -->
<button mat-flat-button color="accent">Flat</button>

<!-- Stroked button (outlined, medium emphasis) -->
<button mat-stroked-button>Stroked</button>

<!-- Icon button (circular, for icons only) -->
<button mat-icon-button aria-label="Menu">
  <mat-icon>menu</mat-icon>
</button>

<!-- FAB (Floating Action Button, for primary actions) -->
<button mat-fab extended color="primary">
  <mat-icon>add</mat-icon> New Item
</button>

The color attribute accepts "primary", "accent", or "warn" — mapped to your theme’s palette.

Progress Indicators

<!-- Circular spinner (indeterminate = spinning animation) -->
<mat-progress-spinner mode="indeterminate"></mat-progress-spinner>

<!-- Progress bar (determinate = shows actual percentage) -->
<mat-progress-bar mode="determinate" value="40"></mat-progress-bar>

Use mode="indeterminate" when you don’t know how long an operation will take. Use mode="determinate" with a value when you can track progress (like file upload percentage).

Form Controls

Material form controls follow a consistent pattern with mat-form-field wrapping:

<!-- Text input with outline appearance -->
<mat-form-field appearance="outline">
  <mat-label>Email</mat-label>
  <input matInput type="email" [formControl]="email" required>
  <mat-error *ngIf="email.invalid">Enter a valid email</mat-error>
</mat-form-field>

<!-- Select dropdown -->
<mat-form-field>
  <mat-label>Role</mat-label>
  <mat-select [formControl]="role">
    <mat-option value="admin">Admin</mat-option>
    <mat-option value="user">User</mat-option>
  </mat-select>
</mat-form-field>

<!-- Toggle switch -->
<mat-slide-toggle [formControl]="notifications">
  Enable notifications
</mat-slide-toggle>

<!-- Slider -->
<mat-slider min="0" max="100" step="1">
  <input matSliderInput [formControl]="volume">
</mat-slider>

The pattern is always: mat-form-field > mat-label + [matInput|mat-select|etc] + optional mat-error.

Navigation — Toolbar, Sidenav, and Tabs

<!-- Toolbar (app header) -->
<mat-toolbar color="primary">
  <button mat-icon-button (click)="drawer.toggle()">
    <mat-icon>menu</mat-icon>
  </button>
  <span>My App</span>
  <span class="spacer"></span>
  <button mat-icon-button><mat-icon>account_circle</mat-icon></button>
</mat-toolbar>

<!-- Sidenav (sidebar) -->
<mat-drawer-container>
  <mat-drawer #drawer mode="side" opened>
    <mat-nav-list>
      <a mat-list-item routerLink="/dashboard">
        <mat-icon matListItemIcon>dashboard</mat-icon>
        Dashboard
      </a>
      <a mat-list-item routerLink="/settings">
        <mat-icon matListItemIcon>settings</mat-icon>
        Settings
      </a>
    </mat-nav-list>
  </mat-drawer>
  <mat-drawer-content>
    <router-outlet></router-outlet>
  </mat-drawer-content>
</mat-drawer-container>

<!-- Tabs -->
<mat-tab-group>
  <mat-tab label="Details">
    <p>Content for details tab</p>
  </mat-tab>
  <mat-tab label="History">
    <p>Content for history tab</p>
  </mat-tab>
</mat-tab-group>

The mode attribute on mat-drawer controls how the sidebar behaves:

  • "side" — Pushes content aside (always visible on desktop)
  • "over" — Overlays content (mobile-friendly)
  • "push" — Pushes content while staying on top

Data Table

Material’s data table is a full-featured table with sorting, pagination, and row selection:

<table mat-table [dataSource]="dataSource" matSort>
  <!-- Define each column -->
  <ng-container matColumnDef="id">
    <th mat-header-cell *matHeaderCellDef mat-sort-header>ID</th>
    <td mat-cell *matCellDef="let row">{{row.id}}</td>
  </ng-container>

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

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

<!-- Paginator -->
<mat-paginator [pageSizeOptions]="[5, 10, 25]" showFirstLastButtons></mat-paginator>
// In your component
import { MatTableDataSource } from "@angular/material/table";
import { MatSort } from "@angular/material/sort";
import { MatPaginator } from "@angular/material/paginator";

export class MyTableComponent implements AfterViewInit {
  displayedColumns = ["id", "name"];
  dataSource = new MatTableDataSource(myData);

  @ViewChild(MatSort) sort!: MatSort;
  @ViewChild(MatPaginator) paginator!: MatPaginator;

  ngAfterViewInit() {
    this.dataSource.sort = this.sort;         // Enable sorting
    this.dataSource.paginator = this.paginator;  // Enable pagination
  }

  applyFilter(event: Event) {
    const filterValue = (event.target as HTMLInputElement).value;
    this.dataSource.filter = filterValue.trim().toLowerCase();  // Filter
  }
}

Dialogs and Overlays

Dialogs are modal overlays for confirmations, forms, or detailed information:

import { MatDialog } from "@angular/material/dialog";

@Component({...})
export class MyComponent {
  constructor(private dialog: MatDialog) {}

  openDialog() {
    const dialogRef = this.dialog.open(ConfirmDialogComponent, {
      width: "400px",
      data: { title: "Confirm", message: "Are you sure?" }
    });

    dialogRef.afterClosed().subscribe(result => {
      if (result) {
        // User clicked "Confirm"
        console.log("User confirmed:", result);
      }
    });
  }
}
  • this.dialog.open(ConfirmDialogComponent) — Opens the dialog and returns a reference.
  • width: "400px" — Sets the dialog width.
  • data: { ... } — Passes data to the dialog component (inject via @Inject(MAT_DIALOG_DATA)).
  • afterClosed() — An Observable that emits when the dialog closes, with the result value.

Cards

Cards group related content into a contained unit:

<mat-card>
  <mat-card-header>
    <mat-card-title>Card Title</mat-card-title>
    <mat-card-subtitle>Subtitle</mat-card-subtitle>
  </mat-card-header>
  <img mat-card-image src="photo.jpg" alt="Photo">
  <mat-card-content>
    <p>Card content goes here.</p>
  </mat-card-content>
  <mat-card-actions align="end">
    <button mat-button>SHARE</button>
    <button mat-button color="primary">OPEN</button>
  </mat-card-actions>
</mat-card>

Cards are perfect for dashboards — Durga Antivirus Pro uses Material cards to display scan results, threat summaries, and system status.

Theming

Material Design revolves around three palette colors: primary (main brand), accent (secondary), and warn (errors/alerts).

// styles.scss
@use "@angular/material" as mat;

// Define palettes
$primary: mat.define-palette(mat.$indigo-palette);
$accent: mat.define-palette(mat.$pink-palette, A200, A100, A400);
$warn: mat.define-palette(mat.$red-palette);

// Build the theme
$theme: mat.define-light-theme((
  color: (primary: $primary, accent: $accent, warn: $warn),
  typography: mat.define-typography-config(),
));

// Apply to all Material components
@include mat.all-component-themes($theme);

To create a dark theme, use define-dark-theme instead:

$dark-theme: mat.define-dark-theme((
  color: (primary: $primary, accent: $accent, warn: $warn),
));

You can combine light and dark themes using CSS classes:

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

Common Mistakes

1. Using *ngIf instead of mat-error

mat-error only shows when the control is invalid and has been touched. Using *ngIf manually breaks the integration.

2. Forgetting MatFormFieldModule import

Each Material component requires its module import. MatFormFieldModule is required for all form controls.

3. Not wrapping input elements correctly

Input elements must have matInput directive and be inside <mat-form-field>. Plain <input> won’t get Material styling.

4. Missing aria-label on icon buttons

Icon buttons have no visible text. Always add aria-label for accessibility. Screen readers rely on it.

5. Overriding Material styles without proper specificity

Material uses Angular’s view encapsulation. Use ::ng-deep sparingly. Instead, use the [style] binding or custom CSS classes with proper specificity.

6. Not configuring @ViewChild for sort and paginator on tables

Sort and paginator require @ViewChild references and must be assigned in ngAfterViewInit, not ngOnInit.

Practice Questions

  1. What does ng add @angular/material do beyond installing the package? It runs a schematic that asks about theme selection, typography, and animations — and configures your project accordingly.

  2. What are the three Material palette colors? Primary (brand color), accent (secondary), and warn (error/alerts).

  3. How do you add sorting to a Material table? Import MatSortModule, add matSort directive to the table, create a @ViewChild(MatSort) reference, and assign it to dataSource.sort in ngAfterViewInit.

  4. What is the purpose of mat-error? To display validation error messages automatically when a form control is invalid and has been touched by the user.

  5. How do you pass data to a dialog component? Use the data option in dialog.open(), then inject @Inject(MAT_DIALOG_DATA) in the dialog component’s constructor.

Challenge

Build a contact management dashboard with: a toolbar, a sidenav with navigation links, a Material card for each contact (avatar, name, email), a search filter, and a dialog for adding new contacts. Use reactive forms inside the dialog.

FAQ

How do I install Angular Material?
ng add @angular/material. Choose a theme and opt-in to typography and animations.
How do I create a dark theme?
Use mat.define-dark-theme() instead of define-light-theme() in your styles.scss. Apply it with a CSS class.
What is the difference between mat-button and mat-raised-button?
mat-button is flat with no shadow (low emphasis). mat-raised-button has a shadow (medium emphasis).
How do I add pagination to a Material table?
Add mat-paginator below the table, import MatPaginatorModule, create @ViewChild(MatPaginator), and assign dataSource.paginator.
How do I create a custom theme?
Define palettes with mat.define-palette(), build a theme with define-light-theme() or define-dark-theme(), and include with all-component-themes().
What modules do I need to import?
Each component has its own module (MatButtonModule, MatCardModule, MatFormFieldModule, etc.). Import only what you use.

Try It Yourself

Create a new project with Angular Material and build a simple task dashboard:

// app.component.ts — starter
import { Component } from "@angular/core";
import { MatToolbarModule } from "@angular/material/toolbar";
import { MatCardModule } from "@angular/material/card";
import { MatButtonModule } from "@angular/material/button";
import { MatIconModule } from "@angular/material/icon";

@Component({
  selector: "app-root",
  standalone: true,
  imports: [
    MatToolbarModule, MatCardModule, MatButtonModule, MatIconModule
  ],
  template: `
    <mat-toolbar color="primary">
      <span>Task Dashboard</span>
    </mat-toolbar>

    <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 16px; padding: 20px;">
      <mat-card *ngFor="let task of tasks">
        <mat-card-header>
          <mat-card-title>{{ task.title }}</mat-card-title>
          <mat-card-subtitle>{{ task.status }}</mat-card-subtitle>
        </mat-card-header>
        <mat-card-actions align="end">
          <button mat-button color="warn">Delete</button>
        </mat-card-actions>
      </mat-card>
    </div>
  `
})
export class AppComponent {
  tasks = [
    { title: "Set up CI/CD", status: "In Progress" },
    { title: "Write documentation", status: "Todo" },
    { title: "Fix login bug", status: "Done" }
  ];
}

Run ng serve to see the result. Then add a dialog for creating new tasks.

What’s Next

TutorialDescription
https://tutorials.dodatech.com/frameworks/angular/angular-forms/Combine Material form controls with reactive forms
https://tutorials.dodatech.com/frameworks/angular/angular-routing/Add Material navigation to your routed app
https://tutorials.dodatech.com/frameworks/angular/reference/Angular API reference and cheatsheet

Related topics: CSS, CSS Flexbox, Sass.

What’s Next

Congratulations on completing this Material tutorial! Here’s where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Apply what you learned by building something real
  • Explore related topics — Check out other tutorials in the same category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro