Styling and UI Libraries

This section documents modern, standalone Angular — signals, the built-in @if / @for / @switch control flow, @defer, typed reactive forms, provideHttpClient, functional guards and interceptors, and server-side rendering with hydration — as described by the official documentation at angular.dev, which is the reference these pages are written and verified against.

This content was generated with the assistance of AI and should be verified against angular.dev before being relied on in production. Angular ships a major release roughly every six months and its APIs continue to evolve: the examples here target the current major release; where a consulted source disagrees with the current documentation, the documentation wins and the difference is noted.

This section’s bibliography lists the reference material consulted while preparing these pages.

Angular scopes styles to a component by default, so most of an app’s CSS lives next to the component it styles. This page covers the built-in styling options and then surveys the component libraries — free and open-source first, commercial suites last.

Styling options

Component styles and view encapsulation

Styles listed in styles (inline) or styleUrl / styleUrls (files) are scoped to the component. ViewEncapsulation controls how: Emulated (default) rewrites selectors and adds per-component attributes so rules do not leak in or out; None adds the rules to the document globally; ShadowDom renders into a real shadow root. Inside those styles, :host targets the component’s own element, :host-context(.selector) styles it based on an ancestor, and ::ng-deep forces a rule through to child components — ::ng-deep is deprecated, so prefer a CSS custom property or a global rule instead.

import { Component, ViewEncapsulation } from '@angular/core';

@Component({
  selector: 'app-badge',
  template: `<span class="dot"></span><ng-content />`,
  styles: `
    :host { display: inline-block; }
    :host(.danger) .dot { background: red; }
    :host-context(.dark-theme) { color: #eee; }
  `,
  encapsulation: ViewEncapsulation.Emulated,
})
export class Badge {}

App-wide rules go in the global stylesheet listed under styles in angular.json (src/styles.css or src/styles.scss by default). See styling components.

Sass

Scaffold a workspace with Sass by passing --style=scss to ng new (or set it as a schematic default); the CLI then compiles .scss component and global stylesheets with no extra configuration.

ng new my-app --style=scss
// badge.component.scss
$brand: #1976d2;

:host {
  display: inline-block;
  .dot { background: $brand; }
}

See the Sass Reference for the language itself.

Tailwind CSS

Install Tailwind CSS per its Angular framework guide, then use utility classes directly in templates. Bind them conditionally with [class] (an object or string), [ngClass], or [class.name].

npm install tailwindcss @tailwindcss/postcss postcss
# add the @tailwindcss/postcss plugin to .postcssrc.json, then
# @import "tailwindcss"; in src/styles.css
<button
  class="rounded px-4 py-2 text-white"
  [class]="{ 'bg-blue-600 hover:bg-blue-700': !busy(), 'bg-gray-400': busy() }">
  Save
</button>

CSS-in-JS and dynamic theming

CSS-in-JS libraries are uncommon in Angular — scoped component styles already give the colocation and isolation they provide elsewhere. For values that change at runtime, set a CSS custom property with [style.--name] and consume it from the component stylesheet.

<div class="chart" [style.--bar-color]="color()" [style.--bar-height.px]="height()"></div>
.chart { background: var(--bar-color); height: var(--bar-height); }

NgClass / NgStyle vs. [class.x] / [style.x]

NgClass and NgStyle accept an object, array, or string and were the pre-v16 way to toggle several classes or styles at once. The native bindings [class.name], [class], [style.prop], and [style] now cover the same ground, are faster, and need no import — prefer them; reach for NgClass / NgStyle only for a dynamic map whose keys are not known ahead of time.

<!-- preferred -->
<p [class.active]="isActive()" [style.color]="isActive() ? 'green' : 'gray'">Status</p>

<!-- NgClass / NgStyle: import { NgClass, NgStyle } from '@angular/common' -->
<p [ngClass]="classMap()" [ngStyle]="styleMap()">Status</p>

See the NgClass / NgStyle notes in the directives guide.

Component libraries — free and open-source

Each ships ready-made, accessible components so you are not rebuilding dialogs, menus, and data tables by hand. They are listed roughly in order of adoption.

Angular Material (MIT)

Angular Material is the first-party Material Design library, built on the Angular CDK and by far the most widely used. Install it with ng add, which also wires up Material 3 (mat.theme) theming and typography.

ng add @angular/material
import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';

@Component({
  selector: 'app-demo',
  imports: [MatButtonModule],
  template: `<button mat-flat-button (click)="save()">Save</button>`,
})
export class Demo {
  save(): void {}
}

This is a pointer only — see Angular Material for the full treatment of theming and components.

PrimeNG (MIT)

PrimeNG is a large free suite of roughly 90 components with design-token theming configured through providePrimeNG(). Only the optional templates (starter admin layouts) are paid; every component is free.

import { ApplicationConfig } from '@angular/core';
import { providePrimeNG } from 'primeng/config';
import Aura from '@primeng/themes/aura';

export const appConfig: ApplicationConfig = {
  providers: [providePrimeNG({ theme: { preset: Aura } })],
};
import { Component } from '@angular/core';
import { ButtonModule } from 'primeng/button';

@Component({
  selector: 'app-demo',
  imports: [ButtonModule],
  template: `<p-button label="Save" (onClick)="save()" />`,
})
export class Demo {
  save(): void {}
}

ng-bootstrap (MIT) and ngx-bootstrap (MIT)

ng-bootstrap and ngx-bootstrap both re-implement Bootstrap 5 widgets as native Angular components with no jQuery and no Bootstrap JavaScript. Add the Bootstrap CSS, then use the components.

ng add @ng-bootstrap/ng-bootstrap
import { Component, inject } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';

@Component({
  selector: 'app-demo',
  template: `
    <button class="btn btn-primary" (click)="open(tpl)">Open</button>
    <ng-template #tpl let-modal>
      <div class="modal-body">Confirm?</div>
      <button class="btn btn-secondary" (click)="modal.close()">Close</button>
    </ng-template>
  `,
})
export class Demo {
  private readonly modalService = inject(NgbModal);
  open(content: unknown): void {
    this.modalService.open(content);
  }
}

See the Bootstrap Reference for the underlying design system.

NG-ZORRO (MIT)

NG-ZORRO is an Ant Design port with a rich enterprise component set — notably nz-table and nz-form.

ng add ng-zorro-antd
import { Component } from '@angular/core';
import { NzButtonModule } from 'ng-zorro-antd/button';

@Component({
  selector: 'app-demo',
  imports: [NzButtonModule],
  template: `<button nz-button nzType="primary" (click)="save()">Save</button>`,
})
export class Demo {
  save(): void {}
}

Taiga UI (Apache-2.0)

Taiga UI is a large, modular, Angular-native kit split into @taiga-ui/core, /kit, /addon-* packages so you pull in only what you use.

ng add @taiga-ui/cdk @taiga-ui/core @taiga-ui/kit
import { Component } from '@angular/core';
import { TuiButton } from '@taiga-ui/core';

@Component({
  selector: 'app-demo',
  imports: [TuiButton],
  template: `<button tuiButton type="button" (click)="save()">Save</button>`,
})
export class Demo {
  save(): void {}
}

spartan/ui (MIT)

spartan/ui ships unstyled, accessible primitives (the brain packages) plus copy-in, Tailwind-styled components (helm) that you own and edit — the Angular analogue of Radix Primitives plus shadcn/ui.

npx nx g @spartan-ng/cli:ui button
import { Component } from '@angular/core';
import { HlmButtonDirective } from '@spartan-ng/ui-button-helm';

@Component({
  selector: 'app-demo',
  imports: [HlmButtonDirective],
  template: `<button hlmBtn (click)="save()">Save</button>`,
})
export class Demo {
  save(): void {}
}

Clarity (MIT) and Nebular (MIT)

Clarity is VMware’s design system with Angular components and a data grid. Nebular is a theming-focused kit with an auth module. Both are less actively maintained than the libraries above — check recent release activity before adopting.

import { Component } from '@angular/core';
import { ClrButtonModule } from '@clr/angular';

@Component({
  selector: 'app-demo',
  imports: [ClrButtonModule],
  template: `<button class="btn btn-primary" (click)="save()">Save</button>`,
})
export class Demo {
  save(): void {}
}

The Angular CDK alone (MIT)

The Angular CDK provides the unstyled behaviour behind a design system — Overlay, the a11y package, drag-and-drop, virtual scroll, BreakpointObserver, Portal, and more. Use it directly to build bespoke components without adopting a full visual library.

ng add @angular/cdk
import { Component, inject } from '@angular/core';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { toSignal } from '@angular/core/rxjs-interop';
import { map } from 'rxjs';

@Component({
  selector: 'app-demo',
  template: `@if (handset()) { <p>Compact layout</p> }`,
})
export class Demo {
  private readonly bp = inject(BreakpointObserver);
  readonly handset = toSignal(
    this.bp.observe(Breakpoints.Handset).pipe(map((r) => r.matches)),
  );
}

Commercial suites — paid / licensed

These are licensed products; they are listed last and without examples.

  • Kendo UI for Angular — a broad commercial component and data-grid suite from Progress Telerik.

  • Ignite UI for Angular — Infragistics' commercial suite, focused on high-performance grids and charts.

  • Syncfusion — a very large commercial suite; a free community licence is available for individuals and small teams under a revenue threshold.

  • AG Grid — the community edition is MIT-licensed; the enterprise grid features (row grouping, pivoting, server-side row model, and more) are paid.

Accessibility

A component library gives you accessible primitives, but you still own the wiring: label every control, manage focus in dialogs and menus, honour prefers-reduced-motion, and test with a keyboard and a screen reader. The CDK a11y package helps — LiveAnnouncer for polite/assertive announcements, cdkTrapFocus to keep focus inside an open dialog, and FocusMonitor to track how an element gained focus.

import { Component, inject } from '@angular/core';
import { A11yModule, LiveAnnouncer } from '@angular/cdk/a11y';

@Component({
  selector: 'app-dialog',
  imports: [A11yModule],
  template: `
    <div cdkTrapFocus role="dialog" aria-label="Confirm delete">
      <button (click)="confirm()">Delete</button>
    </div>
  `,
})
export class Dialog {
  private readonly announcer = inject(LiveAnnouncer);
  confirm(): void {
    this.announcer.announce('Item deleted', 'assertive');
  }
}

See Web Accessibility for conformance levels and validation tools.