Question

Angular ngClass Conditional Class Binding Explained

cssjavascriptangularangular-templateangular-ng-class

Question

I am using Angular to conditionally apply an active class to list items, but my template throws this error:

Cannot read property 'remove' of undefined at BrowserDomAdapter.removeClass

Here is the code:

<ol>
  <li *ngClass="{ active: step === 'step1' }" (click)="step = 'step1'">Step1</li>
  <li *ngClass="{ active: step === 'step2' }" (click)="step = 'step2'">Step2</li>
  <li *ngClass="{ active: step === 'step3' }" (click)="step = 'step3'">Step3</li>
</ol>

What is wrong with this Angular code, and how should conditional class binding be written correctly?

Short Answer

By the end of this page, you will understand how Angular conditional class binding works, when to use ngClass versus [class.className], and why using the wrong binding syntax can cause runtime errors in templates.

Concept

Angular lets you add or remove CSS classes dynamically based on component state. This is called conditional class binding.

In Angular, ngClass is a directive, not a structural directive. That means it should be used like a normal property binding:

<li [ngClass]="{ active: condition }"></li>

A common mistake is to write *ngClass instead of [ngClass].

<li *ngClass="{ active: condition }"></li>

The * prefix is only for structural directives such as:

  • *ngIf
  • *ngFor
  • *ngSwitchCase

Structural directives change the DOM structure by creating or removing elements. ngClass does not create or remove elements. It only changes the classes on an existing element.

Because of that, Angular expects different behavior when it sees *. Using *ngClass makes Angular treat it like a structural directive, which leads to incorrect internal handling and can cause runtime errors like the one shown.

This matters in real programming because class bindings are used everywhere:

  • highlighting active navigation items
  • showing validation states
  • toggling themes
  • displaying selected tabs
  • marking loading or error states

If the binding syntax is wrong, your UI can fail at runtime even though the idea is correct.

Mental Model

Think of an HTML element like a box that already exists on the page.

  • A structural directive with * decides whether the box exists at all.
  • A class binding decides what label or color sticker gets attached to that box.

So:

  • *ngIf = create or remove the box
  • [ngClass] = keep the box, but change its CSS classes

Using *ngClass is like telling Angular: “Treat this class change like element creation logic.” That confuses Angular because ngClass is not built for that job.

Take Quiz

Syntax and Examples

The correct syntax for conditional classes in Angular is:

<li [ngClass]="{ active: step === 'step1' }">Step 1</li>

You can also use direct class binding for a single class:

<li [class.active]="step === 'step1'">Step 1</li>

Corrected version of your example

<ol>
  <li [ngClass]="{ active: step === 'step1' }" (click)="step = 'step1'">Step1</li>
  <li [ngClass]="{ active: step === 'step2' }" (click)="step = 'step2'">Step2</li>
  <li [ngClass]="{ active: step === 'step3' }" (click)="step = 'step3'">Step3</li>
</ol>

Simpler version for one class

<ol>
  <li [class.active]="step === 'step1'" (click)="step = 'step1'">Step1</li>
  <li [class.active]="step === 'step2'" (click)="step = 'step2'">Step2</li>
  <li [class.active]="step === 'step3'" (click)="step = 'step3'">Step3</li>
</ol>

Component example

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

@Component({
  selector: 'app-steps',
  templateUrl: './steps.component.html'
})
export class StepsComponent {
  step = 'step1';
}

Explanation

  • step stores the current active step.
  • When a user clicks a list item, step is updated.
  • Angular re-evaluates the expression.
  • The active class is added only to the matching item.

Step by Step Execution

Consider this template:

<li [class.active]="step === 'step2'" (click)="step = 'step2'">Step2</li>

And this component state:

step = 'step1';

What happens step by step

  1. Angular renders the <li> element.

  2. It checks the expression:

    step === 'step2'
    
  3. Since step is currently 'step1', the result is false.

  4. Angular does not add the active class.

  5. The user clicks Step2.

  6. The click handler runs:

    step = 'step2'
    
  7. Angular change detection runs again.

  8. It re-checks the expression:

    step === 'step2'
    
  9. Now the result is true.

  10. Angular adds the active class to that <li>.

Small trace example

initial step = 'step1'
check: 'step1' === 'step2' => false
class applied? no

click happens
step becomes 'step2'
check: 'step2' === 'step2' => true
class applied? yes

This is the basic pattern behind tabs, menus, selected items, and active states.

Real World Use Cases

Conditional class binding is used in many common Angular interfaces.

Navigation menus

Highlight the current page or selected section.

<a [class.active]="currentPage === 'home'">Home</a>

Tab components

Show which tab is selected.

<button [class.active]="selectedTab === 'profile'">Profile</button>

Form validation

Mark invalid inputs visually.

<input [class.invalid]="emailTouched && !emailValid">

Loading and status indicators

Apply styles based on state.

<div [ngClass]="{ loading: isLoading, error: hasError }"></div>

Selected items in a list

<li [class.selected]="item.id === selectedId">{{ item.name }}</li>

These patterns make the UI react automatically to component data.

Take Quiz

Real Codebase Usage

In real Angular projects, developers often choose between [class.className] and [ngClass] based on complexity.

Use [class.className] for one class

This is the cleanest option when you only need to toggle a single class.

<button [class.disabled]="isSaving">Save</button>

Use [ngClass] for multiple classes

<div [ngClass]="{
  active: isActive,
  disabled: isDisabled,
  error: hasError
}"></div>

Common patterns in real code

Guarding visual states

<div [class.hidden]="!user"></div>

Validation styling

<input [ngClass]="{
  'is-invalid': control.touched && control.invalid,
  'is-valid': control.touched && control.valid
}">

State-driven UI

<span [class.online]="user.status === 'online'"></span>

Reusable components

A component may expose inputs that drive classes:

@Input() isPrimary = false;
@Input() isLarge = false;
<button [ngClass]="{ primary: isPrimary, large: isLarge }">
  Click
</button>

The main idea is that classes should come from state, not manual DOM manipulation.

Common Mistakes

1. Using *ngClass instead of [ngClass]

Broken code:

<li *ngClass="{ active: isActive }">Item</li>

Correct code:

<li [ngClass]="{ active: isActive }">Item</li>

Why it fails:

  • * is for structural directives.
  • ngClass is an attribute directive.

2. Forgetting quotes around string values

Broken code:

<li [class.active]="step === step1">Step1</li>

Correct code:

<li [class.active]="step === 'step1'">Step1</li>

Without quotes, Angular treats step1 as a variable or property name.

3. Mixing assignment with comparison

Broken code:

<li [class.active]="step = 'step1'">Step1</li>

Correct code:

<li [class.active]="step === 'step1'">Step1</li>

Use === when checking a condition.

4. Using ngClass when simple class binding is enough

This is not wrong, but it can be unnecessarily verbose.

Verbose:

<li [ngClass]="{ active: isActive }">Item</li>

Cleaner:

<li [class.active]="isActive">Item</li>

5. Expecting CSS to work without defining the class

Angular may correctly apply the class, but if your stylesheet has no .active rule, nothing will look different.

Example:

.active {
  font-weight: bold;
  color: blue;
}

6. Using complex logic directly in templates

Possible but harder to maintain:

<div [class.active]="user && user.role === 'admin' && !isLocked && currentPage === 'dashboard'"></div>

Better:

get isAdminDashboardActive(): boolean {
  return !!this.user && this.user.role === 'admin' && !this.isLocked && this.currentPage === 'dashboard';
}
<div [class.active]="isAdminDashboardActive"></div>

Comparisons

ApproachUse caseExampleNotes
[class.active]Toggle one class[class.active]="isActive"Simple and readable
[ngClass] with objectToggle multiple classes[ngClass]="{ active: isActive, error: hasError }"Best for several conditions
[ngClass] with stringApply fixed or computed class names[ngClass]="'active highlight'"Useful when classes come from code
[ngClass] with arrayCombine several class values[ngClass]="['active', sizeClass]"Good for composition
class="active"Always apply a classclass="active"No condition involved
*ngIfAdd/remove element from DOM*ngIf="isVisible"Not a class binding

*ngIf vs [ngClass]

Feature*ngIf[ngClass]
PurposeCreate/remove elementAdd/remove CSS classes
Uses *YesNo
Changes DOM structureYesNo
Common useHide/show sectionsStyle states

When to choose which

  • Use [class.active] for one class.
  • Use [ngClass] for several classes.
  • Use *ngIf when the element should not exist at all.
Take Quiz

Cheat Sheet

<!-- One conditional class -->
<div [class.active]="isActive"></div>

<!-- Multiple conditional classes -->
<div [ngClass]="{ active: isActive, error: hasError }"></div>

<!-- Class from string -->
<div [ngClass]="currentClass"></div>

<!-- Class from array -->
<div [ngClass]="['btn', sizeClass]"></div>

Rules

  • Use [ngClass], not *ngClass
  • Use * only with structural directives like *ngIf and *ngFor
  • Use [class.className] for one class
  • Use === to compare values in conditions
  • Quote string literals like 'step1'

Good examples

<li [class.active]="step === 'step1'"></li>
<li [ngClass]="{ active: step === 'step1' }"></li>

Bad example

<li *ngClass="{ active: step === 'step1' }"></li>

Quick fix for the original problem

<ol>
  <li [ngClass]="{ active: step === 'step1' }" (click)="step = 'step1'">Step1</li>
  <li [ngClass]="{ active: step === 'step2' }" (click)="step = 'step2'">Step2</li>
  <li [ngClass]="{ active: step === 'step3' }" (click)="step = 'step3'">Step3</li>
</ol>

FAQ

Why does *ngClass cause an Angular error?

Because * tells Angular to treat the directive as structural, but ngClass is not structural. It should be bound with [ngClass].

Should I use [ngClass] or [class.active]?

Use [class.active] when toggling one class. Use [ngClass] when applying multiple classes or building classes dynamically.

Is ngClass the same as class?

No. class applies fixed classes, while ngClass lets Angular add or remove classes based on component data.

Can I use a function inside ngClass?

Yes, but keep template logic simple. If the function is expensive or unclear, compute the value in the component instead.

What is the difference between *ngIf and [ngClass]?

*ngIf adds or removes an element from the DOM. [ngClass] keeps the element and only changes its CSS classes.

Why is my class not visibly changing?

The class may be applied correctly, but your CSS may not define any styles for it. Check your stylesheet.

Do I need to initialize step in the component?

It is a good idea. Initializing step makes the initial UI state predictable and easier to debug.

Take Quiz

Mini Project

Description

Build a simple Angular step navigation component that highlights the currently selected step. This project demonstrates conditional class binding, click event handling, and state-driven UI updates.

Goal

Create a three-step navigation where clicking a step updates the active class correctly.

Requirements

  • Create a component with a step property
  • Render three clickable steps in the template
  • Apply an active class only to the selected step
  • Change the selected step when the user clicks an item
  • Add CSS so the active step looks visually different
Take Quiz

Keep learning

Related questions

View more