Skip to content
Angular ng directives 4 min read

ngClass & ngStyle

Static class and style attributes only get you so far. Real UIs react to state: a button turns green when valid, a row highlights when selected, a progress bar grows as a percentage changes. Angular gives you two attribute directives — ngClass and ngStyle — that bind CSS classes and inline styles to component state, so the DOM stays in sync with your signals automatically. They sit alongside Angular’s simpler [class.x] and [style.x] bindings, and knowing when to reach for each keeps templates clean and fast.

The simple bindings come first

Before reaching for ngClass, remember that Angular has built-in property bindings for a single class or style. They are the most direct option and should be your default when you toggle one thing.

<!-- Toggle a single class on a boolean -->
<button [class.active]="isActive()">Save</button>

<!-- Set a single style property -->
<div [style.width.px]="width()"></div>
<div [style.color]="textColor()"></div>

[class.active] adds the active class when the expression is truthy and removes it otherwise. The [style.width.px] syntax binds a numeric value with a unit suffix. Reach for ngClass/ngStyle when you need to drive several classes or styles from one expression.

Using ngClass

ngClass adds and removes CSS classes based on the value you bind. It accepts three shapes: a string, an array of strings, or an object whose keys are class names and whose values are booleans.

import { Component, signal, computed } from '@angular/core';
import { NgClass } from '@angular/common';

@Component({
  selector: 'app-alert',
  standalone: true,
  imports: [NgClass],
  template: `
    <div [ngClass]="alertClasses()">{{ message() }}</div>
    <button (click)="cycle()">Next severity</button>
  `,
})
export class AlertComponent {
  message = signal('Disk space is running low.');
  severity = signal<'info' | 'warning' | 'error'>('warning');

  // Object form: each key is toggled by its boolean value
  alertClasses = computed(() => ({
    alert: true,
    'alert-info': this.severity() === 'info',
    'alert-warning': this.severity() === 'warning',
    'alert-error': this.severity() === 'error',
  }));

  cycle() {
    const order = ['info', 'warning', 'error'] as const;
    const next = (order.indexOf(this.severity()) + 1) % order.length;
    this.severity.set(order[next]);
  }
}

When severity is 'warning', the rendered element is:

Output:

<div class="alert alert-warning">Disk space is running low.</div>

The three accepted forms behave like this:

Bound valueExampleResult
String[ngClass]="'box shadow'"Adds both box and shadow
Array[ngClass]="['box', theme()]"Adds box plus the dynamic theme class
Object[ngClass]="{ active: isOn() }"Adds active only when isOn() is truthy

ngClass is additive: it never clobbers classes set statically in the class attribute. A static class="card" and [ngClass]="{ selected: chosen() }" coexist happily on the same element.

Computing the object in a computed() signal (as above) is preferable to building it inline in the template. It keeps the template readable and recomputes only when a dependency actually changes.

Using ngStyle

ngStyle sets multiple inline CSS properties from an object. Keys are CSS property names (camelCase or dash-case both work) and values are the strings or numbers to apply. As with classes, you can append a unit to the key.

import { Component, signal, computed } from '@angular/core';
import { NgStyle } from '@angular/common';

@Component({
  selector: 'app-meter',
  standalone: true,
  imports: [NgStyle],
  template: `
    <div class="track">
      <div class="fill" [ngStyle]="barStyles()"></div>
    </div>
    <input type="range" min="0" max="100" [value]="percent()"
           (input)="percent.set(+$any($event.target).value)" />
  `,
  styles: [`.track { width: 200px; background: #eee; } .fill { height: 12px; }`],
})
export class MeterComponent {
  percent = signal(40);

  barStyles = computed(() => ({
    'width.%': this.percent(),
    backgroundColor: this.percent() > 80 ? '#d33' : '#3a3',
    transition: 'width 150ms ease',
  }));
}

At 40 percent the fill div renders as:

Output:

<div class="fill" style="width: 40%; background-color: rgb(51, 170, 51); transition: width 150ms ease;"></div>

Note the 'width.%' key — the .% (or .px, .em, .rem) suffix tells Angular which unit to append. Without a unit suffix the value is used verbatim, which is why backgroundColor and transition pass through unchanged.

ngClass vs ngStyle at a glance

ngClassngStyle
TargetsCSS class listInline style attribute
Best forReusable named styles defined in CSSOne-off dynamic values (computed width, color)
SpecificityLives in stylesheets, easy to overrideInline, high specificity, hard to override
AcceptsString / array / objectObject only

Prefer ngClass whenever the visual states are nameable and finite — they belong in CSS where designers can edit them. Reserve ngStyle for genuinely dynamic numeric values like positions, widths, or interpolated colors that cannot be enumerated as classes.

Best practices

  • Use [class.x] and [style.x] for a single class or property; reach for ngClass/ngStyle only when binding several at once.
  • Build the bound object in a computed() signal rather than inline in the template, so it recomputes only when dependencies change.
  • Keep visual states in CSS via ngClass instead of hardcoding colors and sizes with ngStyle — it keeps styling overridable and themeable.
  • Remember ngClass is additive and merges with the static class attribute; you don’t need to repeat static classes inside it.
  • Always supply a unit suffix (width.px, width.%) for numeric style values, or pass a fully formed string.
  • Import NgClass / NgStyle explicitly in standalone components — they are no longer globally available without CommonModule.
Last updated June 14, 2026
Was this helpful?