Angular Material — UI Components Complete Guide
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
Installation
ng add @angular/materialRunning this command does three things:
- Installs the
@angular/materialand@angular/cdkpackages - Asks you to choose a pre-built theme (indigo-pink, deeppurple-amber, rose-red, custom)
- Asks whether to include typography (recommended: yes) and animations (recommended: yes)
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
What does
ng add @angular/materialdo beyond installing the package? It runs a schematic that asks about theme selection, typography, and animations — and configures your project accordingly.What are the three Material palette colors? Primary (brand color), accent (secondary), and warn (error/alerts).
How do you add sorting to a Material table? Import
MatSortModule, addmatSortdirective to the table, create a@ViewChild(MatSort)reference, and assign it todataSource.sortinngAfterViewInit.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.How do you pass data to a dialog component? Use the
dataoption indialog.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
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
| Tutorial | Description |
|---|---|
| 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